// // file name: win.C // written by Ting-jen Yen // // The primary file for display thread. #include #include #include #include #include #include #include #include #include #define X_MAIN #include "window.h" #include "display.h" #include "tcpstream.h" #include "block_item.h" #include "sparse_mask.h" #include "sparse_2.h" #include "GLOBAL_VARIABLE.h" #define MIN_WIDTH 128 #define MIN_HEIGHT 128 #define rel_to_abs(val, lev) (int)(((val) >> ((lev)/10)) * table[(lev)%10]) typedef unsigned char byte; // // Variables used to handle color allocation. // int bytePerScanline; byte r[256],g[256],b[256]; int nfcols; int ncols; int numcols; unsigned long freecols[256]; unsigned long cols[256]; int fc2pcol[256]; int byte_per_pixel; int AllocColor(Display *, Window, Colormap); // // Functions defined in fill.C // int fill_direct(unsigned char *, int, int, int, int, int, int); int fill_best(unsigned char *, int, int, int, int, int, int); int fill_new(unsigned char *, int, int, int, int, int, int); int fill_ultra_fast(unsigned char *, int, int, int, int, int, int); static int change = 0; Window view_win; // // This is the function running as display thread. // It is a long function and I should have broken it // up in pieces. Also, some of the variables could // and should be put together into structures. // It consists of all the user-input and display // parts. In order to rewrite this function into // functions more manageable size, it would require // put many of the share variables together into // one structure. // // I just converted it from some old X windows program // using C. Then it keeps growing with new added features. // // -- Ting-jen Yen // void *display_thread(void *) { Window quit_button, nav_win, disp_area, time_area, direct_area; Pixmap pixmap, pixmap2; XImage *theImage, *theImage2; Visual *visual = CopyFromParent; void SendReq(int, int, int, int); int (*fill_now)(unsigned char *, int, int, int, int, int, int); Bool event_filter(Display *, XEvent *, XPointer); void copyimage(XImage *, int, int, int, int, int, int); void set_time(char *, time_t); int m_level(int, int); int image_w, image_h, no_event; int min_width, min_height; int rect_x, rect_y, rect_w, rect_h; int current_x, current_y; // current location on the "view_win" int win_w, win_h; // the size of "view_win" int req_x, req_y; // the location of the requested area int motion_x, motion_y, last_motion_x, last_motion_y; int zoom_x, zoom_y; int pan_x, pan_y; int gc_idx, indct; Time pan_time; Time motion_time; time_t start_time, last_time, curr_time; int current_level; // The current resolution level int inside_quit; char output_buffer[100], time_buffer[10]; int status = NONE; int b_pixel; const char *line = "*"; // the content of these three variables are const char *zoom_in_only = ";"; // not important. Only their const char *zoom_out_only = ","; // address matters. const char *const direct_none[]={NULL}; const char *const direct_view[]={"Mouse Commands:", line, "LEFT CLICK:", " get detail", "LEFT DRAG:", " panning", zoom_in_only, "MIDDLE BUTTON:", zoom_in_only, " zoom in", zoom_in_only, " continuously", zoom_out_only, "RIGHT BUTTON:", zoom_out_only, " zoom out", zoom_out_only, " continuously", NULL}; const char *const direct_nav[]={"Mouse Commands:", line, "LEFT CLICK:", " jump to", zoom_in_only, "MIDDLE BUTTON:", zoom_in_only, " zoom in", zoom_out_only, "RIGHT BUTTON:", zoom_out_only, " zoom out", NULL}; const char *const direct_quit[]={"Mouse Commands:", line, "LEFT CLICK:", " quit program", NULL}; char label[80]; char **direct; void draw_direct(char **, const char *, const char *, const char *, int, int, Window); // initialize some variables ncols = -1; last_motion_x = last_motion_y = motion_x = motion_y = 0; motion_time = 0; current_level = 0; // waiting for the lowest level of image loaded // // Initialize the sizes of windows. And specify // the function used to fill. It is either // fill_new() or fill_best(). fill_direct() // will work, but much slower. fill_ultra_fast() // will use only the lowest resolution data, // and should not be put here unless when // used for debugging. // fill_now = fill_new; win_w = 450; win_h = 450; last_time = start_time = time(NULL); W_init(win_w+188, win_h+64); sprintf(label, "Foveal point client (depth: %d)", byte_per_pixel * 8); XStoreName(display, window, label); if (byte_per_pixel == 3) b_pixel = 4; else b_pixel = 1; // // Initialize color tables. Actually, the colors are already allocated // in function W_init(), here we only use it to set up certain variables. // numcols = ncols = 256; for (int i = 0 ; i < 255; i++) { r[i] = i &0xe0; g[i] = (i << 3) & 0xe0; b[i] = (i << 6) & 0xc0; } // // Make white color. // r[255] = 0xff; g[255] = 0xff; b[255] = 0xff; AllocColor(display, window, colormap); // cout << "Detect Depth:" <max_bounds.ascent+3, output_buffer, strlen(output_buffer)); // // Wait for the initiate data arrive. // After that, setup some variables whose values depend // on the initiate data. // sem_wait( &DATA_READY); sem_post( &DATA_READY); sem_wait( &NEW_DATA_READY); image_w = C[current_level] .get_non_padding_width(); image_h = C[current_level] .get_non_padding_height(); bytePerScanline = C[0] .get_non_padding_height(); levels = HEADER.NOS_LEVEL; min_width = C[levels] .get_non_padding_width(); min_height = C[levels] .get_non_padding_height(); int max_level = m_level(win_w, win_h); int max_level_1 = (max_level / 10) * 10; pixmap2 = XCreatePixmap(display, DefaultRootWindow(display), min_width, min_height, byte_per_pixel * 8); theImage2 = XGetImage(display, pixmap2, 0, 0, min_width, min_height, 0L, ZPixmap); // Draw the lowest resolution image into the // XImage structure which is corresponding to // the nav_win window. fill_ultra_fast((unsigned char *)theImage2->data, 0, min_height - 1, 0, min_width - 1, levels, theImage2->bytes_per_line); sem_post( &NEW_DATA_READY); // // nav_win is the window to display the whole image // in lowest resolution so the user can find the // area the view_win window easily. We can create it // only after we get some basic information of the image // nav_win = CreateCanvas(win_w+44, 10, min_width, min_height, window, ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask | LeaveWindowMask); time_area = CreateCanvas(win_w+44, min_height+20, 110, 40, window, ExposureMask); direct_area = CreateCanvas(win_w+44, min_height+65, 125, 180 , window, ExposureMask); curr_time = last_time = time(NULL); set_time(time_buffer, curr_time - start_time); XDrawString(display, time_area, gcx, 3, font_info1->max_bounds.ascent+3, "Time elapsed:", 13); XDrawString(display, time_area, gcx, 3, font_info1->max_bounds.ascent+23, time_buffer, strlen(time_buffer)); direct = (char **)direct_none; /* for (int i = 0, j = 0 ; i < direct_size ; i++) { if (direct[i]) { XDrawImageString(display, direct_area, gcx, 3, font_info1->max_bounds.ascent+3 + j*15, direct[i], strlen(direct[i])); j ++; } else { XDrawLine(display, direct_area, gcx, 0, j * 15, 125, j * 15); } } */ XMapWindow(display, nav_win); XMapWindow(display, time_area); XMapWindow(display, direct_area); XMapRaised(display, window); // Resize the window to fit the navigate window in. // int side_width = (min_width > 125) ? min_width : 125; XResizeWindow(display, window, win_w+ side_width+60, win_h+64); // // Draw the image on the XImage structure corresponding // to the view_win window // sem_wait( &DATA_READY); fill_now((unsigned char *)theImage->data, 0, win_h-1, 0, win_w-1, current_level, theImage->bytes_per_line); sem_post( &DATA_READY); // // Setup the first request into queue // SendReq(win_w >> 1, win_h >> 1, 35, current_level); // // Copy the XImage structures into the corresponding windows. // XPutImage(display, view_win, gcx, theImage, 0, 0, 0, 0, win_w, win_h); XPutImage(display, nav_win, gcx, theImage2, 0, 0,0,0, min_width, min_height); rect_x = (int) ((current_x >> (levels-current_level/10)) * table[current_level%10] + .5); rect_y = (int) ((current_y >> (levels-current_level/10)) * table[current_level%10] + .5); rect_w = (int) ((win_w >> (levels-current_level/10)) * table[current_level%10] + .5); rect_h = (int) ((win_h >> (levels-current_level/10)) * table[current_level%10] + .5); // XDrawRectangle(display, nav_win, gcx, rect_x, rect_y, rect_w, rect_h); XFlush(display); // // Set beginning position // current_x = current_y = 0; no_event = change = 0; gc_idx = 0; indct = 0; // Begin the loop of fetch Xwindow events while (1) { // // variable: // event: this variable is to store the X window event // I received. // XEvent event; no_event ++; if ((curr_time = time(NULL)) != last_time) { last_time = curr_time; set_time(time_buffer, curr_time - start_time); XDrawImageString(display, time_area, gcx, 3, font_info1->max_bounds.ascent+3, "Time elapsed:", 13); XDrawImageString(display, time_area, gcx, 3, font_info1->max_bounds.ascent+23, time_buffer, strlen(time_buffer)); gc_idx ^= 1; indct ++; } if (indct > 0) { indct = 0; XDrawRectangle(display, nav_win, (gc_idx > 0) ? gcx : gc_inv, rect_x, rect_y, rect_w, rect_h); } // Fetch events in the queue, if there is any. while (1) { int spec = 4; if (!XCheckIfEvent(display, &event, event_filter, (XPointer) &spec)) if (!XCheckIfEvent(display, &event, event_filter, (XPointer) &status)) break; // // Check for ConfigureNotify event, which happened // after window has been moved or resized. // We only handle resize here, other events of // this class will be discarded. // if (event.type == EnterNotify) { if (event.xcrossing.window == view_win) direct = (char **)direct_view; else if (event.xcrossing.window == nav_win) direct = (char **)direct_nav; else if (event.xcrossing.window == quit_button) direct = (char **)direct_quit; draw_direct(direct, line, zoom_in_only, zoom_out_only, current_level, (event.xcrossing.window == nav_win) ? max_level_1 :max_level, direct_area); } else if (event.type == LeaveNotify) { direct = (char **)direct_none; draw_direct(direct, line, zoom_in_only, zoom_out_only, current_level, max_level, direct_area); } else if (event.type == ConfigureNotify && event.xconfigure.window == window) { int old_width, old_height, min_width1, min_height1; int old_x0, old_y0, old_x1, old_y1; // // variables: // old_width, old_height: size of view_win before resized. // min_width1, min_height1: temporary variables to store // when doing comparosion of the // size before and after resize. // old_x0, old_y0, old_x1, old_y1: // the coordinates of the box in the nav_win, which // indicate the location of view_win before resize. // we need to calculate these coordinates to redraw // the box. // // First, check if the width is changed, if so, adjust // the position of the "nav_win" window, so nav_win will // keep the proper distance with view_win. old_width = old_height = 0; if (event.xconfigure.width != win_w+side_width + 60) { old_width = win_w; win_w = event.xconfigure.width-(side_width+60); if (win_w < MIN_WIDTH) win_w = MIN_WIDTH; // // Here, because we fix the upper-left corner of our view, we have // to limit the resized window size upon the current // location, besides the image size. It would be // better not always fix on that point, though // it would make the code a little complex. // if (win_w > image_w - current_x) { // win_w = image_w - current_x; current_x = image_w - win_w; if (current_x < 0) { current_x = 0; win_w = image_w; } } if (old_width != win_w) { XMoveWindow(display, nav_win, win_w+44, 10); XMoveWindow(display, time_area, win_w+44, min_height+20); XMoveWindow(display, direct_area, win_w+44, min_height+65); } } // Second, check if the height is changed, if it is, // adjust the positions of the "Quit" button, and // the status window (the window shows how many data // have been transmitted.) // if (event.xconfigure.height != win_h+64) { old_height = win_h; win_h = event.xconfigure.height - 64; if (win_h < MIN_HEIGHT) win_h = MIN_HEIGHT; if (win_h > image_h - current_y) { current_y = image_h - win_h; // win_h = image_h - current_y; if (current_y < 0) { current_y = 0; win_h = image_h; } } if (old_height != win_h) { XMoveWindow(display, quit_button, 10, win_h+36); XMoveWindow(display, disp_area, 100, win_h+36); } } // // Then, resize the view_win window // if (old_width!=win_w || old_height!=win_h) { XImage *tmpimage; Pixmap new_pixmap; int i0, j0; byte *mem; XResizeWindow(display, view_win, win_w, win_h); // // Here, we have to change the size of the XImage corresponding // to view_win window, first create a new XImage structure, // copy the image from the old one and/or calculate new // exposed area. And after that, we delete the old structure // and replace it by the new one. // new_pixmap = XCreatePixmap(display, DefaultRootWindow(display), win_w, win_h, byte_per_pixel * 8); tmpimage = XGetImage(display, new_pixmap, 0, 0, win_w, win_h, 0L, ZPixmap); min_width1 = (theImage->bits_per_pixel>>3) * (win_w > old_width ? old_width:win_w); sem_wait(&DATA_READY); fill_now((unsigned char *)tmpimage->data, current_y, win_h - 1 + current_y, current_x, win_w - 1 + current_x, current_level, tmpimage->bytes_per_line); sem_post(&DATA_READY); // for (int i = 0 ; i < (win_h > old_height ? old_height:win_h); i++) // bcopy(theImage->data+i*theImage->bytes_per_line, // tmpimage->data+i*tmpimage->bytes_per_line, // min_width1); // // After the resize, the size of the view_win has changed, therefore, // we have to redraw the black box in nav_win which indicates the // visible area in nav_win. Before that, we have to remove the // old black box. // RemoveIndicator(rect_x, rect_y, rect_w, rect_h, min_width, min_height, nav_win, theImage2); rect_w = (int) ((win_w >> (levels-current_level/10)) * table[current_level%10] + .5); rect_h = (int) ((win_h >> (levels-current_level/10)) * table[current_level%10] + .5); indct ++; // XDrawRectangle(display, nav_win, gcx, // rect_x, rect_y, rect_w, rect_h); // // Now, draw the new uncovered area // /* if (old_height < win_h) { sem_wait(&DATA_READY); fill_now(tmpimage->data+old_height*tmpimage->bytes_per_line, old_height+current_y, win_h+current_y-1, current_x, win_w+current_x-1, current_level, tmpimage->bytes_per_line); sem_post(&DATA_READY); } min_height1 = (win_h < old_height) ? win_h : old_height; if (old_width < win_w && old_height > 0) { sem_wait(&DATA_READY); fill_now(tmpimage->data+old_width*b_pixel, current_y, min_height1+current_y-1, old_width+current_x, win_w+current_x-1, current_level, tmpimage->bytes_per_line); sem_post(&DATA_READY); } */ XDestroyImage(theImage); theImage = tmpimage; XFreePixmap(display, pixmap); pixmap = new_pixmap; XPutImage(display, view_win, gcx, tmpimage, 0, 0, 0, 0, win_w, win_h); max_level = m_level(win_w, win_h); max_level_1 = (max_level/10) * 10; } } // // Here we check for "expose" event, which usually occurs // when part of a window is raised from under another window // and needed to redraw that part of window. // else if (event.type == Expose) { // // Redraw part of view_win // if (event.xexpose.window == view_win) { int x, y, w,h; x = event.xexpose.x; y = event.xexpose.y; w = event.xexpose.width; h = event.xexpose.height; XPutImage(display, view_win, gcx, theImage, x, y, x, y, w, h); } else if (event.xexpose.window == time_area) { last_time = curr_time; set_time(time_buffer, curr_time - start_time); XDrawImageString(display, time_area, gcx, 3, font_info1->max_bounds.ascent+3, "Time elapsed:", 13); XDrawImageString(display, time_area, gcx, 3, font_info1->max_bounds.ascent+23, time_buffer, strlen(time_buffer)); } else if (event.xexpose.window == direct_area) { draw_direct(direct, line, zoom_in_only, zoom_out_only, current_level, max_level, direct_area); } // // Redrawing part of nav_win window // else if (event.xexpose.window == nav_win) { int x, y; x = event.xexpose.x; y = event.xexpose.y; XPutImage(display, nav_win, gcx, theImage2, x, y, x, y, event.xexpose.width, event.xexpose.height); // Draw the black box to indicate the current location of // view window indct ++; // XDrawRectangle(display, nav_win, gcx, // rect_x, rect_y, rect_w, rect_h); } else if (event.xexpose.window == quit_button) { int loc; loc = font_info1->max_bounds.ascent + 3; XDrawString(display, quit_button, gcx, 3, loc, "Quit", 4); } else if (event.xexpose.window == disp_area) { int loc; loc = font_info1->max_bounds.ascent + 3; sprintf(output_buffer, "%8lu pixel transfered", ptr_stream->total_byte_read); XDrawImageString(display, disp_area, gcx, 3, loc, output_buffer, strlen(output_buffer)); } } // There is the place to check ClientMessage event. Such // event is usually generated by programs. In this program, // this event is generated when part of a image needs to // be updated because new data has been received. // The data come with this event is the location // and size of the area needs to be updated, (in // the measure of highest resolution.) And the // resolution level that has been changed. // else if (event.type == ClientMessage && event.xclient.data.s[4] >= current_level/10) { // // variable: // x, y, w, h: the absolute coordinates and the size of // the area that has been change. // rel_x, rel_y: the relative coorinates the area // to the view_win. // int x, y, w, h, rel_x, rel_y, loc; if (status == ZOOM_IN || status == ZOOM_OUT) continue; x = event.xclient.data.s[0]; y = event.xclient.data.s[1]; w = event.xclient.data.s[2]; h = event.xclient.data.s[3]; // // Make some adjustment with the parameters so those // values are in the measure of current resolution level. // x = (int)((x >> (current_level /10)) / table[current_level%10] +.5); y = (int)((y >> (current_level /10)) / table[current_level%10] +.5); w = (int)((w >> (current_level /10)) / table[current_level%10] +.5); h = (int)((h >> (current_level /10)) / table[current_level%10] +.5); // // Calculate the relative positions of the area and // the view window. // Note: 'w' and 'h' are the width and height of // the area, but after this calculation, // 'w' and 'h' become the coordinate of // the area on the view window. It is sometime // confusing. // rel_x = x - current_x; rel_y = y - current_y; w += rel_x; h += rel_y; if (rel_x < 0) rel_x = 0; if (rel_y < 0) rel_y = 0; if (w > win_w) w = win_w; if (h > win_h) h = win_h; if (x < current_x) x = current_x; if (y < current_y) y = current_y; // // Re-calculate the affected part // if (w > 0 && h > 0 && x < current_x+w && y < current_y+h) { sem_wait( &DATA_READY); fill_now((unsigned char *)theImage->data + rel_y*theImage->bytes_per_line +rel_x * b_pixel, y, current_y+h-1, x, current_x+w-1, current_level, theImage->bytes_per_line); sem_post( &DATA_READY); w -= rel_x; h -= rel_y; XPutImage(display, view_win, gcx, theImage, rel_x, rel_y, rel_x, rel_y, w, h); } loc = font_info1->max_bounds.ascent + 3; // Since new data are arrived, the count should have // change too. sprintf(output_buffer, "%8lu bytes transfered", ptr_stream->total_byte_read); XDrawImageString(display, disp_area, gcx, 3, loc, output_buffer, strlen(output_buffer)); } // // Check the Quit button // Or probably for some reason, the other thread want to stop. // The variable "inside_quit" will determine which causes the stop. // else if ((inside_quit = pthread_mutex_trylock(&END_THREAD)) == 0 || (event.type == ButtonRelease && event.xbutton.window == quit_button)) { // wait until the network thread catches the disconnect request, // if this comes from user request by pressing "Quit" button. // if (inside_quit) { Q.stop(); sched_yield(); pthread_mutex_lock(&END_THREAD); } // // Refresh the display and clean all the X window events // XSync(display, 1); // // Release all the X-window resources and exit this thread. // XUnloadFont(display, font_info1->fid); XFreeGC(display, gcx); XDestroyImage(theImage); XDestroyImage(theImage2); XFreeColormap(display, colormap); XFreePixmap(display, pixmap); XFreePixmap(display, pixmap2); XCloseDisplay(display); pthread_exit(NULL); } // // We catch button release event only for stop the mouse // motion variables // else if (event.type == ButtonRelease && event.xbutton.window == view_win && status != NONE) { if ((event.xbutton.button == Button1 && status != PAN) || (event.xbutton.button == Button2 && status != ZOOM_IN) || (event.xbutton.button == Button3 && event.xbutton.state & ControlMask != 0 && status != ZOOM_IN) || (event.xbutton.button == Button3 && event.xbutton.state & ControlMask == 0 && status != ZOOM_OUT)) { sched_yield(); continue; } XUndefineCursor(display, view_win); if (status == PAN) { if ((event.xbutton.time - pan_time < 1000) && (abs(pan_x - event.xbutton.x) < 10) && (abs(pan_y - event.xbutton.y) < 10)) { Q.clean(); SendReq(current_x + event.xbutton.x, current_y + event.xbutton.y, 35, current_level); } motion_x = 0; motion_y = 0; } else { SendReq(current_x + (win_w >> 1), current_y + (win_h >> 1), 35, current_level); } status = NONE; } // // Get mouse press event // else if (event.type == ButtonPress) { // // variables: // x_pos, y_pos: the new coordinates the view_win will // look to. // old_level: the resolution level before the zooming. // int x_pos,y_pos, old_level; int old_x0, old_x1, old_y0, old_y1; if (status != NONE) { sched_yield(); continue; } // // Only invoked only when on view_win or nav_win // if (event.xbutton.window != view_win && event.xbutton.window != nav_win) continue; old_level = current_level; // // Button 1 pressed in view window, prepare to catch // motion event and pan the display. // if (event.xbutton.window == view_win && event.xbutton.button == Button1) { XDefineCursor(display, view_win, hand_cursor); status = PAN; pan_x = last_motion_x = event.xbutton.x; pan_y = last_motion_y = event.xbutton.y; pan_time = event.xbutton.time; motion_x = last_motion_x + current_x; motion_y = last_motion_y + current_y; sched_yield(); continue; } // // Button 1 pressed in navigator window. Jump the location of the // view window to the new location // if (event.xbutton.window == nav_win && event.xbutton.button == Button1) { x_pos = (int)((event.xbutton.x << (levels-current_level/10)) /table[current_level%10]+.5) -(win_w>>1); y_pos = (int)((event.xbutton.y << (levels-current_level/10)) /table[current_level%10]+.5)-(win_h>>1); } else { // // Button 2 presses. Zoom in if it is not already at the highest // resolution possible. // And if it is button 3, zoom out instead. // if (event.xbutton.button == Button2 || (event.xbutton.button == Button3 && (event.xbutton.state & ControlMask) != 0)) { if (current_level == 0) continue; else { // // Zooming in: increase resolution, (by decreasing resolution // level,) and change the image size. Change the // the upper-left corner. // if (event.xbutton.window == view_win) { last_motion_x = zoom_x = event.xbutton.x; last_motion_y = zoom_y = event.xbutton.y; ZoomIn_View(current_level, x_pos, y_pos, zoom_x, zoom_y, current_x, current_y); draw_direct(direct, line, zoom_in_only, zoom_out_only, current_level, max_level, direct_area); status = ZOOM_IN; } else if (event.xbutton.window == nav_win) { ZoomIn_Nav(current_level, x_pos, y_pos, event.xbutton.x, event.xbutton.y, win_w, win_h); draw_direct((char **)direct_nav, line, zoom_in_only, zoom_out_only, current_level, max_level_1, direct_area); } } } else { // // We only zoom out when the new image size will larger // that the view window size on both width and height. // if (current_level == (levels - 1) *10) continue; else { // // Zooming out. // if (event.xbutton.window == view_win) { last_motion_x = zoom_x = event.xbutton.x; last_motion_y = zoom_y = event.xbutton.y; if (ZoomOut_View(current_level, x_pos, y_pos, win_w, win_h, zoom_x, zoom_y, current_x, current_y)) continue; else { draw_direct(direct, line, zoom_in_only, zoom_out_only, current_level, max_level, direct_area); status = ZOOM_OUT; } } else if (event.xbutton.window == nav_win) { if (ZoomOut_Nav(current_level, x_pos, y_pos, event.xbutton.x, event.xbutton.y, win_w, win_h)) continue; draw_direct((char **)direct_nav, line, zoom_in_only, zoom_out_only, current_level, max_level_1, direct_area); } } } Adjust_Image_Size(current_level, image_w, image_h); } // // We do some manipulation about the new location so // that the edge of the image will stay on the // edge of the view window. // AdjustLocation(x_pos, y_pos, win_w, win_h, image_w, image_h); // // Since the view on the view window has changed, we have // to remove the black rectangle on the nevigate view // and draw a new one later. RemoveIndicator(rect_x, rect_y, rect_w, rect_h, min_width, min_height, nav_win, theImage2); // // send new request from the new location // SendReq(x_pos + (win_w >> 1), y_pos + (win_h >> 1), 35, current_level); // // Since we have change our view of the view window, we have // to calculate the new display from the data. // current_x = x_pos; current_y = y_pos; sem_wait( &DATA_READY); fill_now((unsigned char *)theImage->data, current_y, win_h +current_y -1, current_x, win_w + current_x -1, current_level,theImage->bytes_per_line); sem_post( &DATA_READY); rect_x = (int) ((current_x >> (levels-current_level/10)) * table[current_level%10] + .5); rect_y = (int) ((current_y >> (levels-current_level/10)) * table[current_level%10] + .5); rect_w = (int) ((win_w >> (levels-current_level/10)) * table[current_level%10] + .5); rect_h = (int) ((win_h >> (levels-current_level/10)) * table[current_level%10] + .5); indct ++; // XDrawRectangle(display, nav_win, gcx, rect_x, rect_y, rect_w, rect_h); XPutImage(display, view_win, gcx, theImage, 0, 0, 0, 0, win_w, win_h); } // // Motion event. Happened when either will mouse button pressed or not, // as long as the mouse cursor is inside the view window. // if it is pressed, we will pan the view window, otherwise, // we change the foveate location. // else if (event.type == MotionNotify && event.xmotion.window == view_win) { // Button 1 is pressed. Doing panning now. if (status != NONE) { int tmp_x, tmp_y; tmp_x = last_motion_x - event.xmotion.x; tmp_y = last_motion_y - event.xmotion.y; if (tmp_x * tmp_x + tmp_y * tmp_y < ((win_w * win_h)>>9)) continue; } if ((event.xmotion.state & Button1Mask) && status == PAN) { int x, y, w,h; int diff_x, diff_y; x = current_x; y = current_y; current_x = motion_x - event.xmotion.x; current_y = motion_y - event.xmotion.y; if (current_x < 0) current_x = 0; if (current_y < 0) current_y = 0; if (current_x > image_w - win_w) current_x = image_w-win_w; if (current_y > image_h -win_h) current_y = image_h - win_h; if (x == current_x && y == current_y) continue; last_motion_x = event.xmotion.x; last_motion_y = event.xmotion.y; diff_x = current_x - x; diff_y = current_y - y; diff_x = (diff_x < 0) ? -diff_x:diff_x; diff_y = (diff_y < 0) ? -diff_y:diff_y; // // On some case which will unlikely happen, we pan the window // totally outside of its previous range. Nothing can be // re-used. // if (diff_x > win_w || diff_y > win_h) { sem_wait( &DATA_READY); fill_now((unsigned char *)theImage->data, current_y, current_y+win_h-1, current_x, current_x + win_w -1, current_level, theImage->bytes_per_line); sem_post( &DATA_READY); } else { // // In most case, if not always, there would be some portion // of the new image on view window is the same as the old one, // we can copy those part and save some time-consuming // calculation on those pixels. // copyimage(theImage, x, y, current_x, current_y, win_w, win_h); if (x != current_x) { if (x > current_x) { diff_x = 0; w = x-1; } else { diff_x = x+win_w-current_x; w = current_x + win_w -1; } sem_wait( &DATA_READY); fill_now((unsigned char *)theImage->data +diff_x * b_pixel, current_y, win_h +current_y -1, diff_x+current_x, w, current_level, theImage->bytes_per_line); sem_post( &DATA_READY); } if (y != current_y) { if (y > current_y) { diff_y = 0; h = y-1; } else { diff_y = y+win_h-current_y-1; h = current_y+win_h -1; } diff_x = (x > current_x) ? x-current_x:0; w = ((x > current_x) ? current_x:x) +win_w - 1; sem_wait( &DATA_READY); fill_now((unsigned char *)theImage->data + theImage->bytes_per_line*diff_y+diff_x * b_pixel, diff_y+current_y, h, diff_x+current_x, w, current_level, theImage->bytes_per_line); sem_post( &DATA_READY); } } RemoveIndicator(rect_x, rect_y, rect_w, rect_h, min_width, min_height, nav_win, theImage2); rect_x = (int) ((current_x >> (levels-current_level/10)) * table[current_level%10] + .5); rect_y = (int) ((current_y >> (levels-current_level/10)) * table[current_level%10] + .5); rect_w = (int) ((win_w >> (levels-current_level/10)) * table[current_level%10] + .5); rect_h = (int) ((win_h >> (levels-current_level/10)) * table[current_level%10] + .5); indct ++; // XDrawRectangle(display, nav_win, gcx, rect_x, rect_y, rect_w, rect_h); XPutImage(display, view_win, gcx, theImage, 0, 0, 0, 0, win_w, win_h); continue; } else if (((event.xmotion.state & Button2Mask) || (event.xmotion.state & (Button3Mask | ControlMask)) != (Button3Mask | ControlMask)) && status == ZOOM_IN) { zoom_x = event.xmotion.x; zoom_y = event.xmotion.y; /* int x_pos, y_pos; if (current_level == 0) continue; last_motion_x = zoom_x = event.xmotion.x; last_motion_y = zoom_y = event.xmotion.y; ZoomIn_View(current_level, x_pos, y_pos, zoom_x, zoom_y, current_x, current_y); Adjust_Image_Size(current_level, image_w, image_h); AdjustLocation(x_pos, y_pos, win_w, win_h, image_w, image_h); current_x = x_pos; current_y = y_pos; sem_wait( &DATA_READY); fill_now(theImage->data, current_y, win_h +current_y -1, current_x, win_w + current_x -1, current_level,theImage->bytes_per_line); sem_post( &DATA_READY); RemoveIndicator(rect_x, rect_y, rect_w, rect_h, min_width, min_height, nav_win, theImage2); rect_x = (int) ((current_x >> (levels-current_level/10)) * table[current_level%10] + .5); rect_y = (int) ((current_y >> (levels-current_level/10)) * table[current_level%10] + .5); rect_w = (int) ((win_w >> (levels-current_level/10)) * table[current_level%10] + .5); rect_h = (int) ((win_h >> (levels-current_level/10)) * table[current_level%10] + .5); indct ++; // XDrawRectangle(display, nav_win, gcx, rect_x, rect_y, rect_w, rect_h); XPutImage(display, view_win, gcx, theImage, 0, 0, 0, 0, win_w, win_h); no_event = 0; continue; */ } else if ((event.xmotion.state & Button3Mask) && status == ZOOM_OUT) { zoom_x = event.xmotion.x; zoom_y = event.xmotion.y; /* int x_pos, y_pos; if (current_level == (levels - 1) * 10) continue; zoom_x = event.xmotion.x; zoom_y = event.xmotion.y; if (ZoomOut_View(current_level, x_pos, y_pos, win_w, win_h, zoom_x, zoom_y, current_x, current_y)) continue; Adjust_Image_Size(current_level, image_w, image_h); AdjustLocation(x_pos, y_pos, win_w, win_h, image_w, image_h); current_x = x_pos; current_y = y_pos; sem_wait( &DATA_READY); fill_now(theImage->data, current_y, win_h +current_y -1, current_x, win_w + current_x -1, current_level,theImage->bytes_per_line); sem_post( &DATA_READY); RemoveIndicator(rect_x, rect_y, rect_w, rect_h, min_width, min_height, nav_win, theImage2); rect_x = (int) ((current_x >> (levels-current_level/10)) * table[current_level%10] + .5); rect_y = (int) ((current_y >> (levels-current_level/10)) * table[current_level%10] + .5); rect_w = (int) ((win_w >> (levels-current_level/10)) * table[current_level%10] + .5); rect_h = (int) ((win_h >> (levels-current_level/10)) * table[current_level%10] + .5); indct ++; // XDrawRectangle(display, nav_win, gcx, rect_x, rect_y, rect_w, rect_h); XPutImage(display, view_win, gcx, theImage, 0, 0, 0, 0, win_w, win_h); no_event = 0; continue; */ } // // Change "fovea". // Only send out request when the request waiting list is not too long, // and when the motion is significant. // else if (Q.length() < 9 * levels && status == NONE) { int diff_x0, diff_y0, req_x0, req_y0; Time diff_time; req_x0 =(int)((req_x >> (current_level/10)) / table[current_level%10]+.5); req_y0 =(int)((req_y >> (current_level/10)) /table[current_level%10]+.5); diff_x0 = event.xmotion.x + current_x - req_x0; diff_y0 = event.xmotion.y + current_y - req_y0; diff_time = event.xmotion.time - motion_time; // // if the motion events come too frequent and current motion // is too slight, there is no need to make the work. // if (diff_time < 200 && diff_x0 *diff_x0 + diff_y0 *diff_y0 < ((win_w * win_h) >> 10)) continue; diff_x0 += req_x0 - motion_x; diff_y0 += req_y0 - motion_y; motion_x = event.xmotion.x + current_x; motion_y = event.xmotion.y + current_y; motion_time = event.xmotion.time; if (diff_time < 500 && (((unsigned long)(diff_x0 * diff_x0 + diff_y0 * diff_y0))<<1) < (diff_time * Q.length())) continue; SendReq(motion_x, motion_y, 25, current_level); } } XFlush(display); no_event = 0; } // // When there is no event for a while, increasing request radius // and send a new request from current position. // Note: This part might cause some problem to program with // event-driving prgraming because this part is cause by // lack of event. // if (no_event > 5 && Q.length() <= 1) { no_event = 0; if (status == ZOOM_IN) { int x_pos, y_pos; if (current_level == 0) continue; ZoomIn_View(current_level, x_pos, y_pos, zoom_x, zoom_y, current_x, current_y); Adjust_Image_Size(current_level, image_w, image_h); AdjustLocation(x_pos, y_pos, win_w, win_h, image_w, image_h); current_x = x_pos; current_y = y_pos; sem_wait( &DATA_READY); fill_now((unsigned char *)theImage->data, current_y, win_h +current_y -1, current_x, win_w + current_x -1, current_level,theImage->bytes_per_line); sem_post( &DATA_READY); RemoveIndicator(rect_x, rect_y, rect_w, rect_h, min_width, min_height, nav_win, theImage2); rect_x = (int) ((current_x >> (levels-current_level/10)) * table[current_level%10] + .5); rect_y = (int) ((current_y >> (levels-current_level/10)) * table[current_level%10] + .5); rect_w = (int) ((win_w >> (levels-current_level/10)) * table[current_level%10] + .5); rect_h = (int) ((win_h >> (levels-current_level/10)) * table[current_level%10] + .5); indct ++; // XDrawRectangle(display, nav_win, gcx, rect_x, rect_y, rect_w, rect_h); XPutImage(display, view_win, gcx, theImage, 0, 0, 0, 0, win_w, win_h); draw_direct(direct, line, zoom_in_only, zoom_out_only, current_level, max_level, direct_area); continue; } else if (status == ZOOM_OUT) { int x_pos, y_pos; if (current_level == (levels - 1) * 10) continue; if (ZoomOut_View(current_level, x_pos, y_pos, win_w, win_h, zoom_x, zoom_y, current_x, current_y)) continue; Adjust_Image_Size(current_level, image_w, image_h); AdjustLocation(x_pos, y_pos, win_w, win_h, image_w, image_h); current_x = x_pos; current_y = y_pos; sem_wait( &DATA_READY); fill_now((unsigned char *)theImage->data, current_y, win_h +current_y -1, current_x, win_w + current_x -1, current_level, theImage->bytes_per_line); sem_post( &DATA_READY); RemoveIndicator(rect_x, rect_y, rect_w, rect_h, min_width, min_height, nav_win, theImage2); rect_x = (int) ((current_x >> (levels-current_level/10)) * table[current_level%10] + .5); rect_y = (int) ((current_y >> (levels-current_level/10)) * table[current_level%10] + .5); rect_w = (int) ((win_w >> (levels-current_level/10)) * table[current_level%10] + .5); rect_h = (int) ((win_h >> (levels-current_level/10)) * table[current_level%10] + .5); indct ++; // XDrawRectangle(display, nav_win, gcx, rect_x, rect_y, rect_w, rect_h); XPutImage(display, view_win, gcx, theImage, 0, 0, 0, 0, win_w, win_h); draw_direct(direct, line, zoom_in_only, zoom_out_only, current_level, max_level, direct_area); continue; } else if (status == PAN) continue; else SendReq_R(current_level); // // If the queue is too long, try to eliminate something // in the queue. Those requests would not affect current // content in view window, (happened after we zoom, pan,) // would be removed. // if (Q.length() > 20) Q.reduce(current_x, current_y, win_w, win_h, current_level/10); XFlush(display); sched_yield(); } } return NULL; } // // A function to put event in X event queue to notify // the display thread the location of the data that // has been updated. It is called from the network thread. // void update_map(block_item *item) { XEvent event; event.type = ClientMessage; event.xclient.send_event = 1; event.xclient.window = view_win; event.xclient.display = display; event.xclient.format = 8; event.xclient.data.s[0] = item->st_col << (item->level+1); event.xclient.data.s[1] = item->st_row << (item->level+1); event.xclient.data.s[2] = ((item->en_col - item->st_col+2) << (item->level+1))-1; event.xclient.data.s[3] = ((item->en_row - item->st_row+2) << (item->level+1))-1; event.xclient.data.s[4] = item->level; XPutBackEvent(display, &event); // XSendEvent(display, view_win, 0, 0, &event); // XFlush(display); } // // This funtion doesn't nothing but return 'TRUE' value. // it is used by XCheckIfEvent function // Bool event_filter(Display *dsp, XEvent *e, XPointer p) { int i = * ((int *) p); if (i == 4) return (e->type == ButtonPress); else return (i == NONE || e-> type == MotionNotify || e->type == ButtonRelease || e->type == EnterNotify || e->type == LeaveNotify); } // // This function is to do some internal manipulation of XImage // structure when we want to copy a part of image to another. // // It is used when we pan the window. // void copyimage (XImage *image, int from_x, int from_y, int to_x, int to_y, int win_w, int win_h) { int x_diff, y_diff; // x_diff, y_diff are the difference of the // old and new coordinates. int from, to, size; // 'from' is the memory offset where the // data are moved from. // 'to' is to offset where those data will // move to. And 'size' is the size of the // image block we will move. x_diff = to_x - from_x; y_diff = to_y - from_y; if (x_diff >= 0) { if (y_diff >= 0) { from = image->bytes_per_line * y_diff + ((image->bits_per_pixel * x_diff) >> 3); to = 0; } else { y_diff = -y_diff; from = (image->bits_per_pixel * x_diff) >> 3; to = image->bytes_per_line * y_diff; } } else { x_diff = -x_diff; if (y_diff >= 0) { from = image->bytes_per_line * y_diff; to = (image->bits_per_pixel * x_diff) >> 3; } else { y_diff = -y_diff; from = 0; to = image->bytes_per_line * y_diff + ((image->bits_per_pixel * x_diff) >> 3); } } size = (win_h - y_diff -1) * image->bytes_per_line + ((x_diff * image->bits_per_pixel) >> 3); bcopy(&image->data[from], &image->data[to], size); } void set_time(char *output, time_t interval) { int hour, min, sec; sec = interval % 60; interval /= 60; min = interval % 60; interval /= 60; if (interval > 99) sprintf(output, "**:%02d:%02d", min, sec); else { hour = interval; sprintf(output, "%02d:%02d:%02d", hour, min, sec); } } void draw_direct(char **string, const char *line, const char *in, const char *out, int curr_level, int levels, Window direct_area) { XClearWindow(display,direct_area); XSetLineAttributes(display, gcx, 1, LineSolid, CapProjecting, JoinMiter); for (int i = 0, j = 0 ; string[i] ; i++) { if (string[i] == line) { XDrawLine(display, direct_area, gcx, 0, j * 15 + 2, 125, j * 15 +2); } else { if (string[i] == in) { if (curr_level == 0) i ++; continue; } else if (string[i] == out) { if (curr_level >= levels) i ++; continue; } XDrawImageString(display, direct_area, gcx, 3, font_info1->max_bounds.ascent+3 + j*15, string[i], strlen(string[i])); j ++; } } XSetLineAttributes(display, gcx, 1, LineSolid, CapRound, JoinRound); } int m_level(int win_w, int win_h) { int level = (levels - 1) * 10; int done = 0; for (int i = 0 ; i < levels - 1 && ! done; i ++) { double fact_w = ((double)C[i].get_non_padding_width()) / win_w; double fact_h = ((double)C[i].get_non_padding_height())/ win_h; double min_fact = (fact_w > fact_h) ? fact_h : fact_w; if (min_fact < 2) { for (int j = 9; j >= 0 ; j --) if (min_fact > table[j]) { level = i * 10 + j; done = 1; break; } } } return level; }