/* Copyright 2001-2015 John Wiseman G8BPQ This file is part of LinBPQ/BPQ32. LinBPQ/BPQ32 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. LinBPQ/BPQ32 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses */ // Version 0.0.3.1 July 2016 // Switch to Thunderforest tile server // Version 0.0.4.1 January 2019 // Add option to set IS filter to map view automatically #ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. #define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. #endif #define LINBPQ #include "compatbits.h" #include "BPQAPRS.h" #include #include #include #include #include #include #include #include #include #include #define XK_MISCELLANY #include #include #include #include #include #define LIBCONFIG_STATIC #include "libconfig.h" #include #include #include #define PNG_SKIP_SETJMP_CHECK #include "png.h" #define VOID void #define UCHAR unsigned char #define BOOL int #define BYTE unsigned char #define UINT unsigned int #define TRUE 1 #define FALSE 0 GtkWidget *dialog; GtkWidget *window; GtkWidget *dialog; GtkWidget *window; GtkWidget *box1; GtkWidget *box2; GtkWidget *box3; GtkWidget *hbox; GtkWidget *button; GtkWidget *button2; GtkWidget *checklabel; GtkWidget *check1; GtkWidget *check2; GtkWidget *check3; GtkWidget *check4; GtkWidget *checkhbox; GtkWidget *separator; GtkWidget *table; GtkWidget *vscrollbar; GtkWidget *vscrollbar2; GtkTextBuffer *text; GtkTextBuffer *text2; GtkWidget *entry; GtkWidget *vpaned; GtkWidget *frame1; GtkWidget *frame2; GtkWidget *view; GtkWidget* scrolledwin; GtkWidget *view2; GtkWidget* scrolledwin2; GtkWidget *box10; GtkWidget *menubar; GtkWidget *combo; GtkWidget *label1, *label2; GtkListStore *receiveditems; GtkListStore *sentitems; GtkTreeModel *model; char MyFont[50] = "Monospace 10"; gchar *fontname; char RX_SOCK_PATH[] = "BPQAPRSrxsock"; char TX_SOCK_PATH[] = "BPQAPRStxsock"; int sfd; struct sockaddr_un my_addr, peer_addr; socklen_t peer_addr_size; int maxfd; struct SharedMem * SMEM; UCHAR * Shared; // Start of Shared Mememy UCHAR * StnRecordBase; // Start of Station Records int AutoFilterTimer = 0; #define AUTOFILTERDELAY 20 // 20 secs VOID SecTimer(); void plotLine(int x0, int y0, int x1, int y1, COLORREF rgb); void SelectTXMsg (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data); int LoadImageFile (void * hwnd, char * pstrPathName, png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels, png_color *pBkgColor); BOOL PngLoadImage (char * pstrFileName, png_byte **ppbImageData, png_uint_32 *piWidth, png_uint_32 *piHeight, int *piChannels, png_color *pBkgColor); BOOL RGBToJpegFile(char * fileName, BYTE *dataBuf, UINT widthPix, UINT height, BOOL color, int quality); int XDestroyImage(XImage *ximage); int XLookupString(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, void *status_in_out); void RefreshTXList(); static png_color bkgColor = {127, 127, 127}; struct SEM { UINT Flag; int Clashes; int Gets; int Rels; }; struct SEM Semaphore = {0, 0, 0, 0}; void GetSemaphore(struct SEM * Semaphore) { // // Wait for it to be free // if (Semaphore->Flag != 0) { Semaphore->Clashes++; } loop1: while (Semaphore->Flag != 0) { Sleep(10); } // try to get semaphore if (__sync_lock_test_and_set(&Semaphore->Flag, 1) != 0) // Failed to get it goto loop1; // try again; //Ok. got it Semaphore->Gets++; return; } void FreeSemaphore(struct SEM * Semaphore) { if (Semaphore->Flag == 0) printf("Free Semaphore Called when Sem not held\n"); Semaphore->Rels++; Semaphore->Flag = 0; return; } char * strlop(char * buf, char delim) { // Terminate buf at delim, and return rest of string char * ptr = strchr(buf, delim); if (ptr == NULL) return NULL; *(ptr)++=0; return ptr; } unsigned long _beginthread(void(*start_address)(), unsigned stack_size, VOID * arglist) { pthread_t thread; if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*) arglist) != 0) perror("New Thread"); else pthread_detach(thread); return thread; } int Sleep(int ms) { usleep(ms * 1000); return 0; } struct OSMQUEUE OSMQueue = {NULL,0,0,0}; int OSMQueueCount = 0; static int cxWinSize = 788, cyWinSize = 788; static int cxImgSize = 768, cyImgSize = 768; static int topBorder = 30, bottomBorder = 0; static int leftBorder = 2, rightBorder = 2; static int cImgChannels = 3; static int ImgChannels; int Bytesperpixel = 4; int ExpireTime = 120; int TrackExpireTime = 1440; BOOL SuppressNullPosn = FALSE; BOOL DefaultNoTracks = FALSE; BOOL LocalTime = TRUE; BOOL KM = FALSE; BOOL AddViewToFilter = FALSE; char ISFilter[1000] = "m/50 u/APBPQ*"; int SlowTimer = 0; BOOL CreateJPEG = TRUE; int JPEGInterval = 300; int JPEGCounter = 0; char JPEGFileName[MAX_PATH] = "BPQAPRS/HTML/APRSImage.jpg"; char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; Display * display; Window root, win; GC gc; XImage * image, * popupimage; int SetBaseX = 0; // Lowest Tiles in currently loaded set int SetBaseY = 0; int TileX = 0; int TileY = 0; int Zoom = 2; int MaxZoom = 16; int MapCentreX = 256; int MapCentreY = 256; int MouseX, MouseY; int PopupX, PopupY; double MouseLat, MouseLon; BOOL NeedRefresh = FALSE; int NeedRedraw = 0; int ScrollX = 128; int ScrollY = 128; int WindowX = 100, WindowY = 100; // Position of window on screen int WindowWidth = 788; int WindowHeight = 788; BOOL popupActive = FALSE; BOOL selActive = FALSE; char OSMDir[256] = "BPQAPRS/OSMTiles"; struct STATIONRECORD ** StationRecords = NULL; struct STATIONRECORD * ControlRecord; int StationCount; UCHAR NextSeq = 1; char APRSCall[10]; char LoppedAPRSCall[10]; char BaseCall[10]; // Image chunks are 256 rows of 3 * 256 bytes // Read 8 * 8 files, and copy to a 2048 * 3 * 2048 array. The display scrolls over this area, and // it is refreshed when window approaches the edge of the array. int WIDTH; int HEIGHT; int WIDTHTILES = 4; int HEIGHTTILES = 4; UCHAR * Image = NULL; UCHAR * iconImage = NULL; UCHAR * PopupImage = NULL; BOOL ImageChanged = 0; int RetryCount = 7; int RetryIntervals[] = {0, 512, 256, 128, 64, 32, 16, 8}; // Station Name Font const unsigned char ASCII[][5] = { //const u08 ASCII[][5] = { {0x00, 0x00, 0x00, 0x00, 0x00} // 20 ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~ ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL }; COLORREF Colours[256] = {0, RGB(0,0,255), RGB(0,128,0), RGB(0,128,192), RGB(0,192,0), RGB(0,192,255), RGB(0,255,0), RGB(128,0,128), RGB(128,64,0), RGB(128,128,128), RGB(192,0,0), RGB(192,0,255), RGB(192,64,128), RGB(192,128,255), RGB(255,0,0), RGB(255,0,255), // 81 RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,128,0)}; struct my_error_mgr { struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* for return to caller */ }; typedef struct my_error_mgr * my_error_ptr; void my_error_exit (j_common_ptr cinfo) { /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ my_error_ptr myerr = (my_error_ptr) cinfo->err; char buffer[JMSG_LENGTH_MAX]; /* Create the message */ (*cinfo->err->format_message) (cinfo, buffer); /* Always display the message. */ printf("JPEG Fatal Error"); /* Return control to the setjmp point */ longjmp(myerr->setjmp_buffer, 1); } int memicmp(unsigned char *a, unsigned char *b, int n) { if (n) { while (n && toupper(*a) == toupper(*b)) n--, a++, b++; if (n) return toupper(*a) - toupper(*b); } return 0; } int stricmp(const unsigned char * pStr1, const unsigned char *pStr2) { unsigned char c1, c2; int v; if (pStr1 == NULL) { return 1; } do { c1 = *pStr1++; c2 = *pStr2++; /* The casts are necessary when pStr1 is shorter & char is signed */ v = tolower(c1) - tolower(c2); } while ((v == 0) && (c1 != '\0') && (c2 != '\0') ); return v; } char * strupr(char* s) { char* p = s; if (s == 0) return 0; while (*p = toupper( *p )) p++; return s; } // Return coorinates in tiles. double long2x(double lon, int z) { return (lon + 180.0) / 360.0 * pow(2.0, z); } double lat2y(double lat, int z) { return (1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z); } double tilex2long(double x, int z) { return x / pow(2.0, z) * 360.0 - 180; } double tiley2lat(double y, int z) { double n = M_PI - 2.0 * M_PI * y / pow(2.0, z); return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); } void GetCornerLatLon(double * TLLat, double * TLLon, double * BRLat, double * BRLon) { int X = ScrollX; int Y = ScrollY; *TLLat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); *TLLon = tilex2long(SetBaseX + (X / 256.0), Zoom); X = ScrollX + cxWinSize; Y = ScrollY + cyWinSize; *BRLat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); *BRLon = tilex2long(SetBaseX + (X / 256.0), Zoom); } void GetMouseLatLon(double * Lat, double * Lon) { int X = ScrollX + MouseX; int Y = ScrollY + MouseY; *Lat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); *Lon = tilex2long(SetBaseX + (X / 256.0), Zoom); } BOOL GetLocPixels(double Lat, double Lon, int * X, int * Y) { // Get the pixel offet of supplied location in current image. // If location is outside current image, return FAlSE int TileX; int TileY; int OffsetX, OffsetY; double FX; double FY; // if TileX or TileY are outside the window, return null FX = long2x(Lon, Zoom); TileX = (int)floor(FX); OffsetX = TileX - SetBaseX; if (OffsetX < 0 || OffsetX > 7) return FALSE; FY = lat2y(Lat, Zoom); TileY = (int)floor(FY); OffsetY = TileY - SetBaseY; if (OffsetY < 0 || OffsetY > 7) return FALSE; FX -= TileX; FX = FX * 256.0; *X = (int)FX + 256 * OffsetX; FY -= TileY; FY = FY * 256.0; *Y = (int)FY + 256 * OffsetY; return TRUE; } int long2tilex(double lon, int z) { return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z))); } int lat2tiley(double lat, int z) { return (int)(floor((1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z))); } BOOL CentrePositionToMouse(double Lat, double Lon) { // Positions specified location at the mouse int X, Y; SetBaseX = long2tilex(Lon, Zoom) - 2; SetBaseY = lat2tiley(Lat, Zoom) - 2; // Set Location at middle if (GetLocPixels(Lat, Lon, &X, &Y) == FALSE) return FALSE; // Off map ScrollX = X - cxWinSize/2; ScrollY = Y - cyWinSize/2; // Map is now centered at loc cursor was at // Need to move by distance mouse is from centre // if ScrollX, Y are zero, the centre of the map corresponds to 1024, 1024 // ScrollX -= 1024 - X; // Posn to centre // ScrollY -= 1024 - Y; ScrollX += cxWinSize/2 - MouseX; ScrollY += cyWinSize/2 - MouseY; // May Need to move image while(ScrollX < 0) { SetBaseX--; ScrollX += 256; } while(ScrollY < 0) { SetBaseY--; ScrollY += 256; } while(ScrollX > 255) { SetBaseX++; ScrollX -= 256; } while(ScrollY > 255) { SetBaseY++; ScrollY -= 256; } AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs return TRUE; } SOCKADDR_IN destaddr = {0}; unsigned int ipaddr = 0; //char Host[] = "tile.openstreetmap.org"; //char Host[] = "oatile1.mqcdn.com"; //SAT //char Host[] = "otile1.mqcdn.com"; char Host[] = "tile.thunderforest.com"; char mapStyle[64] = "outdoors"; //"neighbourhood mobile-atlas char HeaderTemplate[] = "Accept: */*\r\nHost: %s\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; VOID ResolveThread() { struct hostent * HostEnt; int err; // while (TRUE) { // Resolve Name if needed HostEnt = gethostbyname(Host); if (!HostEnt) { err = WSAGetLastError(); printf("Resolve Failed for %s %d %x", Host, err, err); } else { memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); } // Sleep(60 * 15 * 1000); } } VOID OSMGet(int x, int y, int zoom) { struct OSMQUEUE * OSMRec = malloc(sizeof(struct OSMQUEUE)); GetSemaphore(&Semaphore); OSMQueueCount++; OSMRec->Next = OSMQueue.Next; OSMQueue.Next = OSMRec; OSMRec->x = x; OSMRec->y = y; OSMRec->Zoom = zoom; FreeSemaphore(&Semaphore); } VOID RefreshTile(char * FN, int TileZoom, int Tilex, int Tiley); VOID OSMThread() { // Request a page from OSM char FN[256]; char Tile[80]; struct OSMQUEUE * OSMRec; int Zoom, x, y; SOCKET sock; SOCKADDR_IN sinx; int addrlen=sizeof(sinx); int err; u_long param=1; BOOL bcopt=TRUE; char Request[100]; char Header[256]; UCHAR Buffer[200000]; int Len, InputLen = 0; UCHAR * ptr; int inptr = 0; struct stat STAT; FILE * Handle; destaddr.sin_family = AF_INET; destaddr.sin_port = htons(80); while (TRUE) { while (OSMQueue.Next) { GetSemaphore(&Semaphore); OSMRec = OSMQueue.Next; OSMQueue.Next = OSMRec->Next; OSMQueueCount--; FreeSemaphore(&Semaphore); x = OSMRec->x; y = OSMRec->y; Zoom = OSMRec->Zoom; free(OSMRec); // wsprintf(Tile, "/%02d/%d/%d.png", Zoom, x, y); // wsprintf(Tile, "/tiles/1.0.0/sat/%02d/%d/%d.jpg", Zoom, x, y); // sprintf(Tile, "/tiles/1.0.0/osm/%02d/%d/%d.jpg", Zoom, x, y); sprintf(Tile, "/%s/%d/%d/%d.png?apikey=41ab899ed1fd4d09b11da7caf3a48e1f", mapStyle, Zoom, x, y); sprintf(FN, "%s/%02d/%d/%d.png", OSMDir, Zoom, x, y); if (stat(FN, &STAT) == 0) { printf(" File %s Exists - skipping\n", FN); continue; } printf("Getting %s\n", FN); Len = sprintf(Request, "GET %s HTTP/1.0\r\n", Tile); // Allocate a Socket entry sock=socket(AF_INET,SOCK_STREAM,0); if (sock == INVALID_SOCKET) return; setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); if (connect(sock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) != 0) { err=WSAGetLastError(); // // Connect failed // break; } //GET /15/15810/9778.png HTTP/1.0 //Accept: */* //Host: tile.openstreetmap.org //Connection: close //Content-Length: 0 //User-Agent: APRSIS32(G8BPQ) InputLen = 0; inptr = 0; send(sock, Request, Len, 0); sprintf(Header, HeaderTemplate, Host); send(sock, Header, strlen(Header), 0); while (InputLen != -1) { InputLen = recv(sock, &Buffer[inptr], 200000 - inptr, 0); if (InputLen > 0) inptr += InputLen; else { // File Complete?? if (strstr(Buffer, " 200 OK")) { ptr = strstr(Buffer, "Content-Length:"); if (ptr) { int FileLen = atoi(ptr + 15); ptr = strstr(Buffer, "\r\n\r\n"); if (ptr) { ptr += 4; char Dir[256]; if (FileLen == inptr - (ptr - Buffer)) { // File is OK int cnt; Handle = fopen(FN, "wb"); if (Handle) { fwrite(ptr, 1, FileLen, Handle); fclose(Handle); printf("Tile %s Loaded\n", FN); RefreshTile(FN, Zoom, x, y); break; } if (errno != 2) // Bad Path { printf("Create %s failed %d\n", FN, errno); perror("fopen"); break; } sprintf(Dir, "%s/%02d/%d", OSMDir, Zoom, x); if (mkdir(Dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { printf("Error Creating %s\n", FN); perror("mkdir"); break; } // Retry Create Handle = fopen(FN, "wb"); if (Handle) { fwrite(ptr, 1, FileLen, Handle); fclose(Handle); printf("Tile %s Loaded\n", FN); RefreshTile(FN, Zoom, x, y); break; } printf("Create %s falled\n", FN); perror("fopen"); break; } } } } printf("OSM GET Bad Response %s ", Buffer); sprintf(FN, "%s/DummyTile.jpg", OSMDir); RefreshTile(FN, Zoom, x, y); break; } } closesocket(sock); } // Queue is empty sleep(1); } } double radians(double Degrees) { return M_PI * Degrees / 180; } double degrees(double Radians) { return Radians * 180 / M_PI; } double Distance(double laa, double loa) { double lah = ControlRecord->Lat; double loh = ControlRecord->Lon; double dist; /* 'Great Circle Calculations. 'dif = longitute home - longitute away ' (this should be within -180 to +180 degrees) ' (Hint: This number should be non-zero, programs should check for ' this and make dif=0.0001 as a minimum) 'lah = latitude of home 'laa = latitude of away 'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) 'distance = dis / 180 * pi * ERAD 'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) 'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians */ loh = radians(loh); lah = radians(lah); loa = radians(loa); laa = radians(laa); dist = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; if (KM) dist *= 1.60934; return dist; } double Bearing(double lat2, double lon2) { double lat1 = ControlRecord->Lat; double lon1 = ControlRecord->Lon; double dlat, dlon, TC1; lat1 = radians(lat1); lat2 = radians(lat2); lon1 = radians(lon1); lon2 = radians(lon2); dlat = lat2 - lat1; dlon = lon2 - lon1; if (dlat == 0 || dlon == 0) return 0; TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); TC1 = degrees(TC1); if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; if (dlat > 0) { if (dlon > 0) return -TC1; if (dlon < 0) return 360 - TC1; return 0; } if (dlat < 0) { if (dlon > 0) return TC1 = 180 - TC1; if (dlon < 0) return TC1 = 180 - TC1; // 'ok? return 180; } return 0; } VOID DecodeWXReport(struct APRSConnectionInfo * sockptr, char * WX) { UCHAR * ptr = strchr(WX, '_'); char Type; int Val; if (ptr == 0) return; sockptr->WindDirn = atoi(++ptr); ptr += 4; sockptr->WindSpeed = atoi(ptr); ptr += 3; WXLoop: Type = *(ptr++); if (*ptr =='.') // Missing Value { while (*ptr == '.') ptr++; goto WXLoop; } Val = atoi(ptr); switch (Type) { case 'c': // = wind direction (in degrees). sockptr->WindDirn = Val; break; case 's': // = sustained one-minute wind speed (in mph). sockptr->WindSpeed = Val; break; case 'g': // = gust (peak wind speed in mph in the last 5 minutes). sockptr->WindGust = Val; break; case 't': // = temperature (in degrees Fahrenheit). Temperatures below zero are expressed as -01 to -99. sockptr->Temp = Val; break; case 'r': // = rainfall (in hundredths of an inch) in the last hour. sockptr->RainLastHour = Val; break; case 'p': // = rainfall (in hundredths of an inch) in the last 24 hours. sockptr->RainLastDay = Val; break; case 'P': // = rainfall (in hundredths of an inch) since midnight. sockptr->RainToday = Val; break; case 'h': // = humidity (in %. 00 = 100%). sockptr->Humidity = Val; break; case 'b': // = barometric pressure (in tenths of millibars/tenths of hPascal). sockptr->Pressure = Val; break; default: return; } while(isdigit(*ptr)) { ptr++; } if (*ptr != ' ') goto WXLoop; } struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFount) { int i = 0; struct STATIONRECORD * find; struct STATIONRECORD * ptr; struct STATIONRECORD * last = NULL; int sum = 0; if (StationRecords == 0) return FALSE; if (strlen(Call) > 9) Call[9] = 0; find = *StationRecords; while(find) { if (strlen(find->Callsign) > 9) find->Callsign[9] = 0; if (strcmp(find->Callsign, Call) == 0) return find; last = find; find = find->Next; i++; } // Not found - add on end /* if (AddIfNotFount) { // Get first from station record pool ptr = StationRecordPool; if (ptr) { StationRecordPool = ptr->Next; // Unchain StationCount++; } else { // Get First from Stations ptr = *StationRecords; if (ptr) *StationRecords = ptr->Next; } if (ptr == NULL) return NULL; memset(ptr, 0, sizeof(struct STATIONRECORD)); // EnterCriticalSection(&Crit); if (*StationRecords == NULL) *StationRecords = ptr; else last->Next = ptr; // LeaveCriticalSection(&Crit); // Debugprintf("APRS Add Stn %s Station Count = %d", Call, StationCount); strcpy(ptr->Callsign, Call); ptr->TimeAdded = time(NULL); ptr->Index = i; ptr->NoTracks = DefaultNoTracks; for (i = 0; i < 9; i++) sum += Call[i]; sum %= 20; ptr->TrackColour = sum; ptr->Moved = TRUE; return ptr; } else */ return NULL; } int PopupHeight; int PopupWidth; int PopupLeft; int PopupTop; struct STATIONRECORD * popupStn; struct STATIONRECORD * List[1000] = {0}; VOID CreateStationPopup(struct STATIONRECORD * ptr, int RelX, int RelY) { char Msg[80]; int Len = 130; int Line = 12; struct tm * TM; int x, y; PopupLeft = RelX - 10; PopupTop = RelY - 30; if (PopupLeft + 400 > cxWinSize) PopupLeft = cxWinSize - 405; if (PopupTop + 150> cyWinSize) PopupTop= cyWinSize - 165; popupActive = TRUE; popupStn = ptr; PopupHeight = 200; PopupWidth = 350; XClearArea(display, win, PopupLeft, PopupTop, 350, 200, FALSE); XDrawRectangle(display, win, gc, PopupLeft, PopupTop, 350, 200); x = PopupLeft; y = PopupTop; if (LocalTime) TM = localtime(&ptr->TimeLastUpdated); else TM = gmtime(&ptr->TimeLastUpdated); Len = sprintf(Msg, "Last Heard: %.2d:%.2d:%.2d on Port %d", TM->tm_hour, TM->tm_min, TM->tm_sec, ptr->LastPort); XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Callsign, strlen(ptr->Callsign)); Line += 12; XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Path, strlen(ptr->Path)); Line += 12; // XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Status, 40); // Line += 12; XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); Line += 12; // Item.pszText = ptr->LastPacket; Len = sprintf(Msg, "Distance %6.1f Bearing %3.0f Course %1.0f Speed %3.1f", Distance(ptr->Lat, ptr->Lon), Bearing(ptr->Lat, ptr->Lon), ptr->Course, ptr->Speed); XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); Line += 12; if (ptr->LastWXPacket[0]) { //display wx info struct APRSConnectionInfo temp; memset(&temp, 0, sizeof(temp)); DecodeWXReport(&temp, ptr->LastWXPacket); Len = sprintf(Msg, "Wind Speed %d MPH", temp.WindSpeed); XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); Line += 12; Len = sprintf(Msg, "Wind Gust %d MPH", temp.WindGust); XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); Line += 12; Len = sprintf(Msg, "Wind Direction %d°", temp.WindDirn); XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); Line += 12; Len = sprintf(Msg, "Temperature %d°F", temp.Temp); XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); Line += 12; Len = sprintf(Msg, "Pressure %05.1f", temp.Pressure /10.0); XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); Line += 12; Len = sprintf(Msg, "Humidity %d%%", temp.Humidity); XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); Line += 12; } /* Rain last hour##RAIN_HOUR_IN##" Rain today##RAIN_TODAY_IN##" Rain last 24 hours##RAIN_24_IN##" */ } VOID GetStationFromList(int MouseX, int MouseY) { int RelX = MouseX + leftBorder; int RelY = MouseY + topBorder; int index = (RelY - PopupTop) /12; if (List[index]) { selActive = FALSE; CreateStationPopup(List[index], RelX, RelY); } } VOID FindStationsByPixel(int MouseX, int MouseY) { int j=0; struct STATIONRECORD * ptr = *StationRecords; int RelX = MouseX - ScrollX + leftBorder; int RelY = MouseY - ScrollY + topBorder; if (popupActive || selActive) { // if mouse within popup, leave alone if (RelX > PopupLeft && RelX < (PopupLeft + PopupWidth) && RelY > PopupTop && RelY < (PopupTop + PopupHeight)) return; } while(ptr && j < 999) { if (abs((ptr->DispX - MouseX)) < 4 && abs((ptr->DispY - MouseY)) < 4) List[j++] = ptr; ptr = ptr->Next; } if (j == 0) { if (popupActive) { XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); popupActive = 0; } if (selActive) { XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); selActive = 0; } return; } // If only one, display info popup, else display selection popup if (popupActive || selActive) return; // Already on display if (j == 1) { CreateStationPopup(List[0], RelX, RelY); } else { char Msg[80]; int Line = 12; int i; PopupLeft = RelX - 10; PopupTop = RelY - 30; if (j > 20) j = 20; PopupHeight = j * 12 + 4; PopupWidth = 80; if (PopupLeft + 80 > cxWinSize) PopupLeft = cxWinSize - 85; if (PopupTop + PopupHeight > cyWinSize) PopupTop = cyWinSize - PopupHeight; selActive = TRUE; XClearArea(display, win, PopupLeft, PopupTop, 80, PopupHeight, FALSE); XDrawRectangle(display, win, gc, PopupLeft, PopupTop, 80, PopupHeight); for (i = 0; i < j; i++) { memset(Msg, ' ', 12); memcpy(Msg, List[i]->Callsign, strlen(List[i]->Callsign)); XDrawImageString(display, win, gc, PopupLeft + 2, PopupTop + Line, Msg, 11); Line += 12; } } } VOID DrawCharacter(int X, int Y, int j, unsigned char chr) { // Font is 5 bits wide x 8 high. Each byte of font contains one column, so 5 bytes per char int Pointer, i, c, index, bit, mask; Pointer = ((Y - 5) * WIDTH * Bytesperpixel) + ((X + 11) * Bytesperpixel) + (j * 6 * Bytesperpixel); mask = 1; for (i = 0; i < 2; i++) { for (index = 0 ; index < 6 ; index++) { Image[Pointer++] = 255; // Blank lines above chars Image[Pointer++] = 255; if (Bytesperpixel == 4) { Image[Pointer++] = 255; Pointer++; } } Pointer += (WIDTH - 6) * Bytesperpixel; } // Pointer = ((Y - 3) * 2048 * 3) + (X * 3) + 36 + (j * 18); for (i = 0; i < 7; i++) { Image[Pointer++] = 255; // Blank col between chars Image[Pointer++] = 255; if (Bytesperpixel == 4) { Image[Pointer++] = 255; Pointer++; } for (index = 0 ; index < 5 ; index++) { c = ASCII[chr - 0x20][index]; // Font data bit = c & mask; if (bit) { Image[Pointer++] = 0; Image[Pointer++] = 0; if (Bytesperpixel == 4) { Image[Pointer++] = 0; Pointer++; } } else { Image[Pointer++] = 255; Image[Pointer++] = 255; if (Bytesperpixel == 4) { Image[Pointer++] = 255; Pointer++; } } } mask <<= 1; Pointer += (WIDTH - 6) * Bytesperpixel; } // Pointer = ((Y - 3) * 2048 * 3) + (X * 3) + 36 + (j * 18); mask = 1; for (i = 0; i < 2; i++) { for (index = 0 ; index < 6 ; index++) { Image[Pointer++] = 255; // Blank lines below chars between chars Image[Pointer++] = 255; if (Bytesperpixel == 4) { Image[Pointer++] = 255; Pointer++; } } Pointer += (WIDTH - 6) * Bytesperpixel; } } int DrawStation(struct STATIONRECORD * ptr, BOOL AllStations) { int X, Y, Pointer, i, c, index, bit, mask, calllen, calllenpixels; UINT j; char Overlay; char * nptr; time_t AgeLimit = time(NULL ) - (TrackExpireTime * 60); int SavePointer; if (ptr->Moved == 0 && AllStations == 0) return 0; // No need to repaint if (SuppressNullPosn && ptr->Lat == 0.0) return 0; if (ptr->ObjState == '_') // Killed Object return 0; if (GetLocPixels(ptr->Lat, ptr->Lon, &X, &Y)) { if (X < 12 || Y < 12 || X > (WIDTH - 36) || Y > (HEIGHT - 36)) return 0; // Too close to edges if (ptr->LatTrack[0] && ptr->NoTracks == FALSE) { // Draw Track int Index = ptr->Trackptr; int i, n; int X, Y; int LastX = 0, LastY = 0; for (n = 0; n < TRACKPOINTS; n++) { if (ptr->LatTrack[Index] && ptr->TrackTime[Index] > AgeLimit) { if (GetLocPixels(ptr->LatTrack[Index], ptr->LonTrack[Index], &X, &Y)) { if (LastX) { if (abs(X - LastX) < 600 && abs(Y - LastY) < 600) if (X > 0 && Y > 0 && X < (WIDTH - 5) && Y < (HEIGHT - 5)) plotLine(LastX, LastY, X, Y, Colours[ptr->TrackColour]); } LastX = X; LastY = Y; } } Index++; if (Index == TRACKPOINTS) Index = 0; } } ptr->Moved = 0; ptr->DispX = X; ptr->DispY = Y; // Save for mouse over checks // X and Y are offsets into the pixel data in array Image. Actual Bytes are at Y * 2048 * 3 + (X * 3) // Draw Icon if (Y < 8) Y = 8; if (X < 8) X = 8; nptr = &Image[(((Y - 8) * WIDTH) + X - 8) * Bytesperpixel]; // Center icon on station j = (ptr->iconRow * 21 * 337 * Bytesperpixel) + (ptr->iconCol * 21 * Bytesperpixel) + 3 * Bytesperpixel + (337 * 3 * Bytesperpixel); for (i = 0; i < 16; i++) { memcpy(nptr, &iconImage[j], 16 * Bytesperpixel); nptr += WIDTH * Bytesperpixel; j += 337 * Bytesperpixel; } // If an overlay is specified, add it Overlay = ptr->IconOverlay; if (Overlay) { Pointer = (((Y - 4) * WIDTH) + (X - 3)) * Bytesperpixel; mask = 1; for (index = 0 ; index < 7 ; index++) { Image[Pointer++] = 255; // Blank line above chars Image[Pointer++] = 255; if (Bytesperpixel == 4) { Image[Pointer++] = 255; Pointer++; } } Pointer += (WIDTH - 7) * Bytesperpixel; for (i = 0; i < 7; i++) { Image[Pointer++] = 255; // Blank col Image[Pointer++] = 255; if (Bytesperpixel == 4) { Image[Pointer++] = 255; Pointer++; } for (index = 0 ; index < 5 ; index++) { c = ASCII[Overlay - 0x20][index]; // Font data bit = c & mask; if (bit) { Image[Pointer++] = 0; Image[Pointer++] = 0; if (Bytesperpixel == 4) { Image[Pointer++] = 0; Pointer++; } } else { Image[Pointer++] = 255; Image[Pointer++] = 255; if (Bytesperpixel == 4) { Image[Pointer++] = 255; Pointer++; } } } Image[Pointer++] = 255; // Blank col Image[Pointer++] = 255; if (Bytesperpixel == 4) { Image[Pointer++] = 255; Pointer++; } mask <<= 1; Pointer += (WIDTH - 7) * Bytesperpixel; } for (index = 0 ; index < 7 ; index++) { Image[Pointer++] = 255; // Blank line below chars Image[Pointer++] = 255; if (Bytesperpixel == 4) { Image[Pointer++] = 255; Pointer++; } } Pointer += (WIDTH - 6) * Bytesperpixel; } calllen = strlen(ptr->Callsign); while (calllen && ptr->Callsign[calllen - 1] == ' ') // Remove trailing spaces calllen--; calllenpixels = (calllen + 1) * 6; // Draw Callsign Box Pointer = ((Y - 7) * WIDTH * Bytesperpixel) + ((X + 9) * Bytesperpixel); // Draw | at each end for (j = 0; j < 13; j++) { Image[Pointer] = 0; Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; Image[Pointer] = 0; Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; if (Bytesperpixel == 4) { Image[Pointer] = 0; Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; Pointer++; } Pointer += (WIDTH - 1) * Bytesperpixel; } // Draw Top Line for (i = 0; i < calllenpixels; i++) { Image[Pointer++] = 0; Image[Pointer++] = 0; if (Bytesperpixel == 4) { Image[Pointer++] = 0; Pointer++; } } // Draw Bottom Line Pointer = ((Y - 7) * WIDTH * Bytesperpixel) + ((X + 9) * Bytesperpixel); for (i = 0; i < calllenpixels; i++) { Image[Pointer++] = 0; Image[Pointer++] = 0; if (Bytesperpixel == 4) { Image[Pointer++] = 0; Pointer++; } } // Draw Callsign. for (j = 0; j < calllen; j++) { DrawCharacter(X, Y,j, ptr->Callsign[j]); } ImageChanged = TRUE; return 1; } else { ptr->DispX = 0; ptr->DispY = 0; // Off Screen } return 0; } int RefreshStationMap(BOOL AllStations) { struct STATIONRECORD * ptr = *StationRecords; int blackColor = BlackPixel(display, DefaultScreen(display)); int whiteColor = WhitePixel(display, DefaultScreen(display)); int Changed = 0; char msg[80]; int i = 0, len; while (ptr) { Changed += DrawStation(ptr, AllStations); i++; ptr = ptr->Next; } // NeedRefresh = FALSE; // LastRefresh = time(NULL); // if (RecsDeleted) // RefreshStationList();] len = sprintf(msg, "%d Stations Zoom = %d", i, Zoom); XDrawImageString(display, win, gc, 20, 20, msg, len); StationCount = i; return Changed; } void j_putRGBScanline(BYTE *jpegline, int widthPix, unsigned char *outBuf, int row, int XOffset, int YOffset) { // Offsets are in tiles, not pixels int offset = row * WIDTH * Bytesperpixel; //widthPix int count; unsigned int val; offset += XOffset * 256 * Bytesperpixel; offset += YOffset * 256 * WIDTH * Bytesperpixel; for (count = 0; count < 256; count++) { if (Bytesperpixel == 2) { val = (*(jpegline + count * 3 + 2) >> 3); val |= ((*(jpegline + count * 3 + 1) >> 2) << 5); val |= ((*(jpegline + count * 3 + 0) >> 3) << 11); *(outBuf + offset++) = (val & 0xff); *(outBuf + offset++) = (unsigned char)(val >> 8); } else { *(outBuf + offset++) = *(jpegline + count * 3 + 2); // Blue *(outBuf + offset++) = *(jpegline + count * 3 + 1); // Green *(outBuf + offset++) = *(jpegline + count * 3 + 0); // Red offset++; } } } // // stash a gray scanline // void j_putGrayScanlineToRGB(BYTE *jpegline, int widthPix, BYTE *outBuf, int row) { int offset = row * widthPix * 3; int count; for (count=0;countalloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { /* jpeg_read_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could ask for * more than one scanline at a time if that's more convenient. */ (void) jpeg_read_scanlines(&cinfo, buffer, 1); /* Assume put_scanline_someplace wants a pointer and sample count. */ // asuumer all 3-components are RGBs if (cinfo.out_color_components==3) { j_putRGBScanline(buffer[0], *width, Image, cinfo.output_scanline-1, XOffset, YOffset); } else if (cinfo.out_color_components==1) { // assume all single component images are grayscale j_putGrayScanlineToRGB(buffer[0], *width, Image, cinfo.output_scanline-1); } } /* Step 7: Finish decompression */ (void) jpeg_finish_decompress(&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress(&cinfo); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose(infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.pub.num_warnings is nonzero). */ return 0; } // store a scanline to our data buffer void j_putRGBScanline(BYTE *jpegline, int widthPix, BYTE *outBuf, int row, int X, int Y); void j_putGrayScanlineToRGB(BYTE *jpegline, int widthPix, BYTE *outBuf, int row); VOID LoadImageTile(int Zoom, int startx, int starty, int x, int y); VOID RefreshTile(char * FN, int TileZoom, int Tilex, int Tiley) { // Called when a new tile has been diwnloaded from OSM int StartRow, StartCol; UCHAR * pbImage = NULL; int x, y, i, j; int ImgChannels; if (TileZoom != Zoom) return; // Zoom level has changed x = Tilex - SetBaseX; y = Tiley - SetBaseY; if (x < 0 || x > WIDTHTILES -1 || y < 0 || y > HEIGHTTILES - 1) return; // Tile isn't part of current image; LoadImageTile (Zoom, Tilex, Tiley, x, y); NeedRedraw = 1; // XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); } VOID LoadImageTile(int Zoom, int startx, int starty, int x, int y) { char FN[100]; int i, j; int StartRow; int StartCol; char Tile[100]; UCHAR * pbImage = NULL; int ImgChannels; BOOL JPG=FALSE; struct stat STAT; int cx, cy; int Limit = (int)pow(2, Zoom); /* printf("LoadImage %d %d %d\n", Limit, startx, startx); if (startx < 0) startx = startx + WIDTHTILES; else if (startx > WIDTHTILES - 1) startx = WIDTHTILES - startx; if (starty < 0) starty = starty + HEIGHTTILES; else if (starty > HEIGHTTILES -1 ) starty = HEIGHTTILES - starty; if (startx < 0 || startx > WIDTHTILES) x = WIDTHTILES / 2; if (starty < 0 || y > HEIGHTTILES) starty = HEIGHTTILES /2; printf("LoadImage %d %d %d\n", Limit, startx, starty); */ if ((startx) >= Limit || (starty) >= Limit || startx< 0 || starty < 0) { // printf("Not Loading %d %d %d\n",Limit, startx, startx ); return; //goto NoFile; } // May be PNG or JPG sprintf(Tile, "/%02d/%d/%d.png", Zoom, startx, starty); sprintf(FN, "%s%s", OSMDir, Tile); if (stat(FN, &STAT) == 0) goto gotfile; sprintf(Tile, "/%02d/%d/%d.jpg", Zoom, startx, starty); sprintf(FN, "%s%s", OSMDir, Tile); JPG = TRUE; if (stat(FN, &STAT) == 0) goto gotfile; OSMGet(startx, starty, Zoom); return; gotfile: if (JPG) { JpegFileToRGB(FN, &cx, &cy, x, y); ImgChannels = 3; } else { int offset; int cxImgSize, cyImgSize; UCHAR * ImageSave; LoadImageFile (NULL, FN, &pbImage, &cxImgSize, &cyImgSize, &ImgChannels, &bkgColor); ImgChannels = 4; StartCol = x * Bytesperpixel * 256; StartRow = y * 256; // printf("WIDTH %d Height %d Bytesperpixel = %d x = %d y = %d\n", WIDTH, HEIGHT, Bytesperpixel, x, y); if (pbImage == NULL) { pbImage = malloc(256 * 3 * 256); memset(pbImage, 0x40, 256 * 3 * 256); } ImageSave = pbImage; offset = ((StartRow) * WIDTH * ImgChannels) + StartCol; // printf ("x %d y %d offset %d \n", x, y, offset); for (i = 0; i < 256; i++) { int count, val; offset = ((StartRow + i) * WIDTH * Bytesperpixel) + StartCol; // this does one scan line for (count = 0; count < 256; count++) { if (Bytesperpixel == 2) { val = (*(pbImage + count * 3 + 2) >> 3); val |= ((*(pbImage + count * 3 + 1) >> 2) << 5); val |= ((*(pbImage + count * 3 + 0) >> 3) << 11); Image[offset++] = (val & 0xff); Image[offset++] = (unsigned char)(val >> 8); } else { Image[offset++] = *(pbImage + count * 3 + 2); // Blue Image[offset++] = *(pbImage + count * 3 + 1); // Green Image[offset++] = *(pbImage + count * 3 + 0); // Red offset++; } } pbImage += 768; // used 768 pixels } free(ImageSave); } } VOID LoadImageSet(int Zoom, int TileX, int TileY) { int x, y; if (SetBaseX != TileX || SetBaseY != TileY) { // Only Load if changed SetBaseX = TileX; // Lowest Tiles in currently loaded set SetBaseY = TileY; memset(Image, 0, WIDTH * Bytesperpixel * HEIGHT); XClearWindow(display, win); for (y = 0; y < HEIGHTTILES; y++) { for (x = 0; x < WIDTHTILES; x++) { LoadImageTile(Zoom, TileX + x, TileY + y, x, y); } } RefreshStationMap(TRUE); } XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); } BYTE * ReadIcons(char * fileName, UINT *width, UINT *height) { struct jpeg_decompress_struct cinfo; struct my_error_mgr jmerr; struct jpeg_error_mgr jerr; FILE * infile=NULL; /* source file */ JSAMPARRAY buffer; /* Output row buffer */ int row_stride; /* physical row width in output buffer */ char buf[250]; BYTE *dataBuf; *width=0; *height=0; if ((infile = fopen(fileName, "rb")) == NULL) { return NULL; } cinfo.err = jpeg_std_error(&jerr); if (setjmp(jmerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress(&cinfo); if (infile!=NULL) fclose(infile); return NULL; } jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); (void) jpeg_read_header(&cinfo, TRUE); (void) jpeg_start_decompress(&cinfo); dataBuf = malloc(cinfo.output_width * 4 * cinfo.output_height); memset(dataBuf, 0, cinfo.output_width * 4 * cinfo.output_height); if (dataBuf==NULL) { jpeg_destroy_decompress(&cinfo); fclose(infile); return NULL; } // how big is this thing gonna be? *width = cinfo.output_width; *height = cinfo.output_height; /* JSAMPLEs per row in output buffer */ row_stride = cinfo.output_width * cinfo.output_components; /* Make a one-row-high sample array that will go away when done with image */ buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { int offset; int count; unsigned int val; (void) jpeg_read_scanlines(&cinfo, buffer, 1); offset = (cinfo.output_scanline-1) * cinfo.output_width * Bytesperpixel; for (count = 0; count < cinfo.output_width; count++) { if (Bytesperpixel == 2) { val = (*(buffer[0] + count * 3 + 2) >> 3); val |= ((*(buffer[0] + count * 3 + 1) >> 2) << 5); val |= ((*(buffer[0] + count * 3 + 0) >> 3) << 11); *(dataBuf + offset++) = (val & 0xff); *(dataBuf + offset++) = (unsigned char)(val >> 8); } else { *(dataBuf + offset++) = *(buffer[0] + count * 3 + 2); // Blue *(dataBuf + offset++) = *(buffer[0] + count * 3 + 1); // Green *(dataBuf + offset++) = *(buffer[0] + count * 3 + 0); // Red offset++; } } } (void) jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); return dataBuf; } // store a scanline to our data buffer void ZoomIn() { if (Zoom < 16) { Zoom ++; CentrePositionToMouse(MouseLat, MouseLon); TileX = SetBaseX; TileY = SetBaseY; NeedRefresh = TRUE; } } void ZoomOut() { if (Zoom > 1) { Zoom --; CentrePositionToMouse(MouseLat, MouseLon); TileX = SetBaseX; TileY = SetBaseY; if (Zoom == 1) ScrollX = ScrollY = 0; NeedRefresh = TRUE; } } config_t cfg; config_setting_t *croot, *group; int GetIntValue(config_setting_t * group, char * name, int defaultval) { config_setting_t *setting; setting = config_setting_get_member (group, name); if (setting) return config_setting_get_int (setting); return defaultval; } VOID SaveIntValue(config_setting_t * group, char * name, int value) { config_setting_t *setting; setting = config_setting_add(group, name, CONFIG_TYPE_INT); if(setting) config_setting_set_int(setting, value); } BOOL GetStringValue(config_setting_t * group, char * name, char * value) { const char * str; config_setting_t *setting; setting = config_setting_get_member (group, name); if (setting) { str = config_setting_get_string (setting); strcpy(value, str); return TRUE; } return FALSE; } VOID SaveStringValue(config_setting_t * group, char * name, char * value) { config_setting_t *setting; setting = config_setting_add(group, name, CONFIG_TYPE_STRING); if (setting) config_setting_set_string(setting, value); } enum { COL_FROM = 0, COL_TO, COL_SEQ, COL_TIME, COL_RECEIVED, NUM_COLS } ; void CancelMessageSend (GtkWidget *menuitem, struct APRSMESSAGE * userdata) { /* userdata->Retries = 0; userdata->RetryTimer = 0; userdata->Cancelled = TRUE; UpdateTXMessageLine(userdata); */ } void view_popup_menu_onDoNothing (GtkWidget *menuitem, gpointer userdata) { GtkTreeView *treeview = GTK_TREE_VIEW(userdata); } void view_popup_menu (GtkWidget *treeview, GdkEventButton *event, struct APRSMESSAGE * userdata) { GtkWidget *menu, *menuitem1,*menuitem2 ; char Msg[80]; sprintf(Msg,"Cancel Message Seq %s to %s?", userdata->Seq, userdata->ToCall); menu = gtk_menu_new(); menuitem1 = gtk_menu_item_new_with_label(Msg); menuitem2 = gtk_menu_item_new_with_label("Return"); g_signal_connect(menuitem1, "activate", (GCallback) CancelMessageSend, (gpointer)userdata); g_signal_connect(menuitem2, "activate", (GCallback) view_popup_menu_onDoNothing, treeview); if (userdata->Retries) // Not active so cant cancel gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem1); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem2); gtk_widget_show_all(menu); /* Note: event can be NULL here when called from view_onPopupMenu; * gdk_event_get_time() accepts a NULL argument */ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, (event != NULL) ? event->button : 0, gdk_event_get_time((GdkEvent*)event)); } gboolean view_onButtonPressed (GtkWidget *treeview, GdkEventButton *event, gpointer userdata) { // Right click on TX Message window. If a message is selected, // Pop up a Cancel Message Window if (event->type == GDK_BUTTON_PRESS && event->button == 3) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; if (ptr == 0) return TRUE; // Make sure the entry that was clicked is selected selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint) event->x, (gint) event->y, &path, NULL, NULL, NULL)) { gtk_tree_selection_unselect_all(selection); gtk_tree_selection_select_path(selection, path); gtk_tree_path_free(path); } selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gchar *Seq; gtk_tree_model_get (model, &iter, 1, &Seq, -1); // Find the message while (ptr) { if (strcmp(ptr->Seq, Seq) == 0) { view_popup_menu(treeview, event, ptr); g_free(Seq); return TRUE; } ptr = ptr->Next; } g_free(Seq); g_print ("Msg not found.\n"); } g_print ("no row selected.\n"); } return FALSE; /* we did not handle this */ } gboolean view_onPopupMenu (GtkWidget *treeview, gpointer userdata) { view_popup_menu(treeview, NULL, userdata); return TRUE; /* we handled this */ } static GtkWidget *create_sent_window( void ) { GtkCellRenderer *renderer; GtkTreeModel *model; GtkTreeIter iter; view = gtk_tree_view_new(); gtk_signal_connect (GTK_OBJECT (view), "row_activated", GTK_SIGNAL_FUNC (SelectTXMsg), NULL); g_signal_connect(view, "button-press-event", (GCallback) view_onButtonPressed, NULL); g_signal_connect(view, "popup-menu", (GCallback) view_onPopupMenu, NULL); renderer = gtk_cell_renderer_text_new(); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "To", renderer, "text", 0, NULL); renderer = gtk_cell_renderer_text_new (); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Seq", renderer, "text", 1, NULL); renderer = gtk_cell_renderer_text_new (); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "State", renderer, "text", 2, NULL); renderer = gtk_cell_renderer_text_new (); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Time", renderer, "text", 3, NULL); renderer = gtk_cell_renderer_text_new (); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Sent", renderer, "text", 4, NULL); sentitems = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); model = GTK_TREE_MODEL(sentitems); gtk_tree_view_set_model((GtkTreeView *)view, model); /* The tree view has acquired its own reference to the * model, so we can drop ours. That way the model will * be freed automatically when the tree view is destroyed */ g_object_unref (model); // gtk_container_add (GTK_CONTAINER (window), view2); scrolledwin = gtk_scrolled_window_new(NULL,NULL); gtk_container_set_border_width(GTK_CONTAINER(scrolledwin), 1); // gtk_widget_set_size_request(scrolledwin, 300, 80); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), GTK_SHADOW_IN); // gtk_container_add(GTK_CONTAINER(scrolledwin), view); //tree_view = gtk_tree_view_new(); gtk_container_add(GTK_CONTAINER (scrolledwin), view); //gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (view)); //gtk_widget_show(tree_view); /* gtk_table_attach (GTK_TABLE (table), scrolledwin,0, 1, 0, 1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); */ gtk_widget_show(scrolledwin); return scrolledwin; } GdkPixbuf *create_pixbuf(const gchar * filename) { GdkPixbuf *pixbuf; GError *error = NULL; pixbuf = gdk_pixbuf_new_from_file(filename, &error); if(!pixbuf) { fprintf(stderr, "%s\n", error->message); g_error_free(error); } return pixbuf; } static GtkWidget *create_received_window(void) { GtkCellRenderer *renderer; view2 = gtk_tree_view_new(); // gtk_tree_view_set_fixed_height_mode(view2, TRUE); renderer = gtk_cell_renderer_text_new(); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "From", renderer, "text", COL_FROM, NULL); renderer = gtk_cell_renderer_text_new (); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "To", renderer, "text", COL_TO, NULL); renderer = gtk_cell_renderer_text_new (); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Seq", renderer, "text", COL_SEQ, NULL); renderer = gtk_cell_renderer_text_new (); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Time", renderer, "text", COL_TIME, NULL); renderer = gtk_cell_renderer_text_new (); renderer->ypad = 0; gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Received", renderer, "text", COL_RECEIVED, NULL); receiveditems = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); gtk_tree_view_set_model((GtkTreeView *)view2, (GtkTreeModel *)receiveditems); /* The tree view has acquired its own reference to the * model, so we can drop ours. That way the model will * be freed automatically when the tree view is destroyed */ g_object_unref (receiveditems); scrolledwin2 = gtk_scrolled_window_new(NULL,NULL); gtk_container_set_border_width(GTK_CONTAINER(scrolledwin2), 2); // gtk_widget_set_size_request(scrolledwin2, 300, 80); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin2),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin2), GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(scrolledwin2), view2); gtk_widget_show(scrolledwin2); return scrolledwin2; } char ToCalls[1024] = ""; VOID SendAPRSMessage(const char * Text, char * ToCall); void enter_callback( GtkWidget *widget, GtkWidget *entry ) { const gchar *entry_text; entry_text = gtk_entry_get_text (GTK_ENTRY (entry)); gchar * tocall = strupr(gtk_combo_box_text_get_active_text((GtkComboBoxText *)combo)); char Key[32]; if (strlen(tocall) > 9) tocall[9] = 0; sprintf(Key, "|%s|", tocall); if (tocall) { SendAPRSMessage(entry_text, tocall); // if new call add to combo box if (strstr(ToCalls, Key) == 0) { if (strlen(ToCalls) < 1000) strcat(ToCalls, Key); gtk_combo_box_text_prepend_text ((GtkComboBoxText *)combo, tocall); } g_free(tocall); gtk_entry_set_text (GTK_ENTRY (entry), ""); } } void SelectTXMsg (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) { GtkTreeIter iter; GtkTreeModel *model; struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; if (ptr == 0) return; model = gtk_tree_view_get_model(tree_view); if (gtk_tree_model_get_iter(model, &iter, path)) { gchar *seq; gtk_tree_model_get(model, &iter, 1, &seq, -1); g_print ("Double-clicked row contains seq %s\n", seq); g_free(seq); } return; } VOID GTKThread() { gtk_main(); } int msgWinWidth = 300; int msgWinHeight = 300; int msgWinX = 100; int msgWinY = 100; int Split = 100; // Rx/Tx Window split void frame_callback(GtkWindow *window, GdkEvent *event, gpointer data) { int x, y; char buf[10]; msgWinX = event->configure.x; msgWinY = event->configure.y; msgWinWidth = event->configure.width; msgWinHeight = event->configure.height; // gtk_widget_set_size_request(entry, msgWinWidth - 210 , 20); //gtk_entry_new_with_buffer(text); // gtk_window_set_title(window, buf); // gtk_window_set_title (GTK_WINDOW (window), "BPQAPRS Messaging"); } BOOL OnlyMine = FALSE; BOOL OnlySeq = FALSE; BOOL ShowBulls = FALSE; BOOL AllSSID = FALSE; void check_callback(GtkButton *button, gpointer user_data) { GtkTreeIter iter; struct APRSMESSAGE * ptr = SMEM->Messages; int n = 0; char BaseFrom[10]; OnlyMine = gtk_toggle_button_get_active((GtkToggleButton *)check1); OnlySeq = gtk_toggle_button_get_active((GtkToggleButton *)check2); ShowBulls = gtk_toggle_button_get_active((GtkToggleButton *)check3); AllSSID = gtk_toggle_button_get_active((GtkToggleButton *)check4); // rewite the Message display with new filter if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(receiveditems), &iter, NULL, 0)) { while (gtk_list_store_remove(receiveditems, &iter)) {} } while (ptr) { if (memcmp(ptr->ToCall, "BLN", 3)== 0) if (ShowBulls == TRUE) goto wantit; if (strcmp(ptr->ToCall, APRSCall) == 0) // to me? goto wantit; if (AllSSID) { memcpy(BaseFrom, ptr->ToCall, 10); strlop(BaseFrom, ' '); strlop(BaseFrom, '-'); if (strcmp(BaseFrom, BaseCall) == 0) goto wantit; } if (OnlyMine == FALSE) // Want All if (OnlySeq == FALSE || ptr->Seq[0] != 0) goto wantit; // ignore ptr = ptr->Next; continue; wantit: gtk_list_store_insert_with_values( receiveditems, &iter, -1, COL_FROM, ptr->FromCall, COL_TO, ptr->ToCall, COL_SEQ, ptr->Seq, COL_TIME, ptr->Time, COL_RECEIVED, ptr->Text, -1); ptr = ptr->Next; n++; } if (n) gtk_tree_view_scroll_to_cell((GtkTreeView *)view2, gtk_tree_model_get_path (GTK_TREE_MODEL(receiveditems), &iter), NULL, FALSE, 0, 0); } void button_callback(GtkButton *button, gpointer user_data) { SMEM->ClearRX = 1; } void button2_callback(GtkButton *button, gpointer user_data) { // Clear Sent Messages SMEM->ClearTX = 1; } static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) { // Don't allow window to be closed return TRUE; } void SaveConfig() { memset((void *)&cfg, 0, sizeof(config_t)); config_init(&cfg); croot = config_root_setting(&cfg); gtk_window_get_size(GTK_WINDOW(window), &msgWinWidth, &msgWinHeight); gtk_window_get_position(GTK_WINDOW(window), &msgWinX, &msgWinY); Split = gtk_paned_get_position((GtkPaned *)vpaned); group = config_setting_add(croot, "APRS", CONFIG_TYPE_GROUP); SaveIntValue(group, "Zoom", Zoom); SaveIntValue(group, "SetBaseX", SetBaseX); SaveIntValue(group, "SetBaseY", SetBaseY); SaveIntValue(group, "ScrollX", ScrollX); SaveIntValue(group, "ScrollY", ScrollY); SaveIntValue(group, "WindowX", WindowX); SaveIntValue(group, "WindowY", WindowY); SaveIntValue(group, "WindowWidth", WindowWidth); SaveIntValue(group, "WindowHeight", WindowHeight); SaveIntValue(group, "WIDTHTILES", WIDTHTILES); SaveIntValue(group, "HEIGHTTILES", HEIGHTTILES); SaveIntValue(group, "msgWinWidth", msgWinWidth); SaveIntValue(group, "msgWinHeight", msgWinHeight); SaveIntValue(group, "msgWinX", msgWinX); SaveIntValue(group, "msgWinY", msgWinY); SaveIntValue(group, "Split", Split); SaveIntValue(group, "OnlyMine", OnlyMine); SaveIntValue(group, "OnlySeq", OnlySeq); SaveIntValue(group, "ShowBulls", ShowBulls); SaveIntValue(group, "LocalTime", LocalTime); SaveIntValue(group, "KM", KM); SaveIntValue(group, "AddViewToFilter", AddViewToFilter); SaveStringValue(group, "ISFilter", ISFilter); SaveIntValue(group, "CreateJPEG", CreateJPEG); SaveIntValue(group, "JPEGInterval", JPEGInterval); SaveStringValue(group, "JPEGFileName", JPEGFileName); if(!config_write_file(&cfg, "BPQAPRS.cfg")) printf("Error while writing config file.\n"); else printf("Config Saved\n"); config_destroy(&cfg); printf("%s\n", ToCalls); } // Linux Signal Handlers BOOL Running = TRUE; static void sigterm_handler(int sig) { printf("sigterm\n"); Running = FALSE; } static void sigint_handler(int sig) { SaveConfig(); Running = FALSE; exit(0); } int main(int argc, char *argv[]) { UCHAR * pbImage = NULL; char FN[256]; int fd; int x, y; time_t TimeLoaded = time(NULL); struct stat STAT; char * Env; int SharedSize; double vals[10]; int screen_number, depth, bitmap_pad, status; unsigned long white; unsigned long black; Visual * visual; unsigned int i, j; Pixmap pixmap, popuppixmap; int x11_fd; fd_set in_fds; struct timeval tv; XEvent event; XConfigureEvent xce; XKeyEvent xkeyev; Time lastupevent; char text[256]; long unsigned int key; int LastX, LastY; // Saved mouse position when button down int MovedX, MovedY; double sx, sy; UCHAR * APRSStationMemory; Atom wmDeleteMessage; PangoFontDescription *font_desc; GtkTreeIter iter; #ifndef WIN32 signal(SIGHUP, SIG_IGN); signal(SIGINT, sigint_handler); signal(SIGTERM, sigterm_handler); signal(SIGPIPE, SIG_IGN); #endif printf("G8BPQ APRS Client for Linux Version 0.0.4.3\n"); printf("Copyright © 2004-2019 John Wiseman G8BPQ\n"); printf("APRS is a registered trademark of Bob Bruninga.\n"); printf("This software is based in part on the work of the Independent JPEG Group.\n"); printf("Mapping from OpenStreetMap (http://openstreetmap.org)\n"); printf("Map Tiles Courtesy of thunderforest (www.thunderforest.com)\n\n"); config_init(&cfg); /* Read the file. If there is an error, report it and exit. */ if(!config_read_file(&cfg, "BPQAPRS.cfg")) { fprintf(stderr, "%d - %s\n", config_error_line(&cfg), config_error_text(&cfg)); config_destroy(&cfg); } else { group = config_lookup (&cfg, "APRS"); if (group) { Zoom = GetIntValue(group, "Zoom", 2); TileX = GetIntValue(group, "SetBaseX", 0); TileY = GetIntValue(group, "SetBaseY", 0); ScrollX = GetIntValue(group, "ScrollX", 0); ScrollY = GetIntValue(group, "ScrollY", 0); WindowX = GetIntValue(group, "WindowX", 100); WindowY = GetIntValue(group, "WindowY", 100); WindowWidth = GetIntValue(group, "WindowWidth", 788); WindowHeight = GetIntValue(group, "WindowHeight", 788); HEIGHTTILES = GetIntValue(group, "HEIGHTTILES", 4); WIDTHTILES = GetIntValue(group, "WIDTHTILES", 4); msgWinWidth = GetIntValue(group, "msgWinWidth", 300); msgWinHeight = GetIntValue(group, "msgWinHeight", 300); msgWinX = GetIntValue(group, "msgWinX", 100); msgWinY = GetIntValue(group, "msgWinY", 100); Split = GetIntValue(group, "Split", 100); OnlyMine = GetIntValue(group, "OnlyMine", 0); OnlySeq = GetIntValue(group, "OnlySeq", 1); ShowBulls = GetIntValue(group, "ShowBulls", 0); LocalTime = GetIntValue(group, "LocalTime", 0); KM = GetIntValue(group, "KM", 0); AddViewToFilter = GetIntValue(group, "AddViewToFilter", 0); CreateJPEG = GetIntValue(group, "CreateJPEG", 1); JPEGInterval = GetIntValue(group, "JPEGInterval", 300); GetStringValue(group, "JPEGFileName", JPEGFileName); GetStringValue(group, "ISFilter", ISFilter); } } if (Zoom == 0) Zoom = 2; HEIGHT = HEIGHTTILES * 256; WIDTH = WIDTHTILES * 256; Env = getenv("DISPLAY"); if (Env == NULL) { printf("DISPLAY is not set - can't run without X\n", Env); return 0; } printf("DISPLAY is set to %s\n", Env); if (strstr(Env, "localhost:1")) printf("!!! WARNING !!! X session seems to be tunneled over an SSH session\nThis program will run much faster if you set DISPLAY to the Host running your X Server\n"); // Get shared memory fd = shm_open("/BPQAPRSSharedMem", O_RDWR, S_IRUSR | S_IWUSR); if (fd == -1) { printf("Open APRS Shared Memory Failed\n"); return 0; } else { // Map shared memory object Shared = mmap((void *)APRSSHAREDMEMORYBASE, sizeof(struct STATIONRECORD) * 2, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); if (Shared == MAP_FAILED) { printf("Map APRS Shared Memory Failed\n"); APRSStationMemory = NULL; return 0; } } SMEM = (struct SharedMem *)Shared; SharedSize = SMEM->SharedMemLen; printf("Shared Memory Size %d\n", SharedSize); StnRecordBase = Shared + 32; StationRecords = (struct STATIONRECORD**)StnRecordBase; ControlRecord = (struct STATIONRECORD*)StnRecordBase; memset(APRSCall, 0x20, 9); memcpy(APRSCall, ControlRecord->Callsign, strlen(ControlRecord->Callsign)); printf("LinBPQ Configured with MaxStations %d APRSCall %s\n", ControlRecord->LastPort, APRSCall); memcpy(BaseCall, APRSCall, 10); // Get call less SSID strlop(BaseCall, ' '); strlop(BaseCall, '-'); // Remap with Server's view of MaxStations munmap(APRSStationMemory, sizeof(struct STATIONRECORD) * 2); Shared = mmap((void *)APRSSHAREDMEMORYBASE, SharedSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); if (Shared == MAP_FAILED) { printf("Extend APRS Shared Memory Failed\n"); APRSStationMemory = NULL; return 0; } SMEM->NeedRefresh = 1; // Initial Load of messages maxfd = sfd = socket(AF_UNIX, SOCK_DGRAM, 0); if (sfd == -1) { perror("Socket"); } else { memset(&my_addr, 0, sizeof(struct sockaddr_un)); my_addr.sun_family = AF_UNIX; strncpy(my_addr.sun_path, RX_SOCK_PATH, sizeof(my_addr.sun_path) - 1); memset(&peer_addr, 0, sizeof(struct sockaddr_un)); peer_addr.sun_family = AF_UNIX; strncpy(peer_addr.sun_path, TX_SOCK_PATH, sizeof(peer_addr.sun_path) - 1); unlink(RX_SOCK_PATH); if (bind(sfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1) perror("bind"); } XInitThreads(); ResolveThread(); _beginthread(OSMThread, 0, NULL); display = XOpenDisplay(NULL); if (! display) { printf("Couldn't open X display\n"); return 1; } screen_number = DefaultScreen (display); depth = DefaultDepth (display, screen_number); visual = DefaultVisual (display, screen_number); gc = DefaultGC (display, screen_number); bitmap_pad = BitmapPad (display); white = WhitePixel (display, screen_number); black = BlackPixel (display, screen_number); root = DefaultRootWindow (display); if (depth == 16) Bytesperpixel = 2; Image = malloc(WIDTH * Bytesperpixel * HEIGHT + 100); // Seems past last byte gets corrupt if (mkdir(OSMDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { if (errno != 17) // File exists { printf("Error Creating %s\n", OSMDir); perror("mkdir"); } } for (i = 0; i < 20; i++) { sprintf(FN, "%s/%02d", OSMDir, i); if (mkdir(FN, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { if (errno != 17) // File exists { printf("Error Creating %s\n", FN); perror("mkdir"); } } } // Read Icons iconImage = ReadIcons("BPQAPRS/Symbols.jpg", &x, &y); if (x == 0) printf("Couldn't load Icons\n"); // win = XCreateSimpleWindow (display, root, 50, 50, 800, 800, 0, black, white); win = XCreateWindow (display, root, 50, 50, 788, 788, 0, depth, CopyFromParent, CopyFromParent, 0, 0); XStoreName(display, win, "BPQAPRS Map"); wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); XSetWMProtocols(display, win, &wmDeleteMessage, 1); // XSelectInput(display, win, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask); XSelectInput(display, win, ExposureMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); XSetWindowBackground(display, win, 0xffffff); XClearWindow(display, win); XMapWindow (display, win); XMoveResizeWindow(display, win, WindowX, WindowY, WindowWidth, WindowHeight); image = XCreateImage (display, visual, depth, ZPixmap, 0, NULL, WIDTH, HEIGHT, bitmap_pad, 0); printf("depth : %d\nbitmap_pad : %d\nimage bpp : %d\n", depth, bitmap_pad, image->bits_per_pixel); image->data = Image; pixmap = XCreatePixmap (display, root, WIDTH, HEIGHT, depth); XSetLineAttributes(display, gc, 2, LineSolid, CapNotLast, JoinMiter); SetBaseX = -1; // force reload SetBaseY = -1; LoadImageSet(Zoom, TileX, TileY); // Loads 1024 * 1024 Block x11_fd = ConnectionNumber(display); if (x11_fd > maxfd) maxfd = x11_fd; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW (window), msgWinWidth, msgWinHeight); gtk_widget_set_uposition(GTK_WIDGET(window),msgWinX, msgWinY); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gtk_container_set_border_width (GTK_CONTAINER (window), 10); gtk_window_set_resizable(GTK_WINDOW (window), TRUE); // g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (close_application), NULL); gtk_window_set_title (GTK_WINDOW (window), "BPQAPRS Messaging"); gtk_container_set_border_width(GTK_CONTAINER (window), 0); // Load bpqicon if present if (stat("bpqicon.png", &STAT) == 0) gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("bpqicon.png")); //gtk_window_get_frame_dimensions(GTK_WINDOW(window),&left,&top,&right,&bottom); gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(delete_event), NULL); // g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(frame_callback), NULL); // Create a box for the menu box1 = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (window), box1); checklabel = gtk_label_new(" "); check1 = gtk_check_button_new_with_label ("Show only My Msgs "); check4 = gtk_check_button_new_with_label ("All SSIDs "); check2 = gtk_check_button_new_with_label ("Show only Sequenced Msgs "); check3 = gtk_check_button_new_with_label ("Show Bulls "); button = gtk_button_new_with_label("Clear RX"); button2 = gtk_button_new_with_label("Clear TX"); gtk_toggle_button_set_active((GtkToggleButton *)check1, OnlyMine); gtk_toggle_button_set_active((GtkToggleButton *)check2, OnlySeq); gtk_toggle_button_set_active((GtkToggleButton *)check3, ShowBulls); gtk_toggle_button_set_active((GtkToggleButton *)check4, AllSSID); g_signal_connect(G_OBJECT(check1), "clicked", G_CALLBACK(check_callback), "1"); g_signal_connect(G_OBJECT(check2), "clicked", G_CALLBACK(check_callback), "2"); g_signal_connect(G_OBJECT(check3), "clicked", G_CALLBACK(check_callback), "3"); g_signal_connect(G_OBJECT(check4), "clicked", G_CALLBACK(check_callback), "4"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_callback), "1"); g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(button2_callback), "1"); // hBox for Check boxes checkhbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (checkhbox), checklabel, FALSE, FALSE, 0); gtk_container_add (GTK_CONTAINER (checkhbox), check1); gtk_container_add (GTK_CONTAINER (checkhbox), check4); gtk_container_add (GTK_CONTAINER (checkhbox), check2); gtk_container_add (GTK_CONTAINER (checkhbox), check3); gtk_container_add (GTK_CONTAINER (checkhbox), button); gtk_container_add (GTK_CONTAINER (checkhbox), button2); checklabel = gtk_label_new(" "); gtk_container_add (GTK_CONTAINER (checkhbox), checklabel); gtk_box_pack_start (GTK_BOX (box1), checkhbox, FALSE, FALSE, 0); box10 = gtk_vbox_new (FALSE, 0); // menubar = get_menubar_menu (window); // gtk_box_pack_start (GTK_BOX (box1), menubar, FALSE, TRUE, 1); gtk_container_add (GTK_CONTAINER (box1), box10); gtk_widget_show (window); vpaned = gtk_vpaned_new (); gtk_container_add (GTK_CONTAINER (box10), vpaned); gtk_paned_set_position(GTK_PANED(vpaned), Split); gtk_widget_show (vpaned); /* Now create the contents of the two halves of the window */ frame1 = create_received_window(); gtk_paned_add1 (GTK_PANED (vpaned), frame1); gtk_widget_show (frame1); frame2 = create_sent_window(); gtk_paned_add2 (GTK_PANED (vpaned), frame2); gtk_widget_show (frame2); // separator = gtk_hseparator_new (); // gtk_box_pack_start(GTK_BOX (box1), separator, FALSE, TRUE, 0); box2 = gtk_hbox_new(FALSE, 10); gtk_container_set_border_width(GTK_CONTAINER (box2), 1); gtk_box_pack_start(GTK_BOX (box10), box2, FALSE, FALSE, 0); // set up the text entry line label1 = gtk_label_new(" To"); label2 = gtk_label_new("Message"); combo = gtk_combo_box_text_new_with_entry(); gtk_widget_set_size_request(combo, 100, 10); entry = gtk_entry_new(); // gtk_widget_set_size_request(entry, 100, 20); //gtk_entry_new_with_buffer(text); gtk_entry_set_max_length(GTK_ENTRY(entry), 100); gtk_entry_set_activates_default(GTK_ENTRY (entry), TRUE); g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK(enter_callback), (gpointer)entry); gtk_box_pack_start(GTK_BOX(box2), label1, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box2), combo, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box2), label2, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(box2), entry); gtk_widget_grab_focus(entry); font_desc=pango_font_description_from_string(MyFont); gtk_widget_modify_font (entry, font_desc); gtk_widget_modify_font (combo, font_desc); gtk_widget_modify_font (view, font_desc); gtk_widget_modify_font (view2, font_desc); gtk_widget_show_all (window); gtk_widget_show (window); // Main loop _beginthread(GTKThread, 0, NULL); _beginthread(SecTimer, 0, NULL); AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs while(Running) { unsigned char Msg[256]; int numBytes; struct STATIONRECORD * Station; FD_ZERO(&in_fds); FD_SET(x11_fd, &in_fds); FD_SET(sfd, &in_fds); tv.tv_usec = 0; tv.tv_sec = 1; // Wait for X Event, Message from LinBPQ or a Timer if (select(maxfd+1, &in_fds, 0, 0, &tv)) { if (FD_ISSET(sfd, &in_fds)) { numBytes = recvfrom(sfd, Msg, 256, 0, NULL, NULL); } // may be X event, but pick up later } else { // Handle timer here if (SMEM->NeedRefresh) { SMEM->NeedRefresh = FALSE; // Use Checkbox event to rewrite display check_callback((GtkButton *)check1, "1"); RefreshTXList(); } // NeedRedraw += RefreshStationMap(FALSE); // Draw new or moved stations // Do a full redraw at least evey 2 mins if anything has changed // SlowTimer++; // if (SlowTimer > 40) // 2 Mins // if (NeedRedraw) // NeedRefresh = TRUE; } // Handle XEvents and flush the input while(Running && XPending(display)) { XNextEvent(display, &event); MouseX = event.xbutton.x - leftBorder; MouseY = event.xbutton.y - topBorder; GetMouseLatLon(&MouseLat, &MouseLon); switch (event.type) { case ClientMessage: if (event.xclient.data.l[0] == wmDeleteMessage) Running = FALSE; break; case KeyPress: xkeyev = event.xkey; XLookupString(&event.xkey, text, 255, &key, 0); printf("Key %c Hex %x Code %d %x\n", text[0], text[0], key, key); switch(key) { case XK_Left: WindowX -= 8; XMoveWindow(display, win, WindowX, WindowY); break; case XK_Up: WindowY -= 8; XMoveWindow(display, win, WindowX, WindowY); break; case XK_Right: WindowX += 8; XMoveWindow(display, win, WindowX, WindowY); break; case XK_Down: WindowY += 8; XMoveWindow(display, win, WindowX, WindowY); break; case '-': ZoomOut(); break; case '=': case '+': ZoomIn(); break; } break; case Expose: XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); break; case ConfigureNotify: xce = event.xconfigure; // This event type is generated for a variety of // happenings, so check whether the window has been // resized. WindowX = xce.x; WindowY = xce.y; WindowWidth = xce.width; WindowHeight = xce.height; if (xce.width != cxWinSize || xce.height != cyWinSize) { cxWinSize = xce.width; cyWinSize = xce.height; cxImgSize = cxWinSize - (leftBorder + rightBorder); cyImgSize = cyWinSize - (topBorder + bottomBorder); XClearWindow(display, win); XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); } break; case MotionNotify: FindStationsByPixel(MouseX + ScrollX, MouseY + ScrollY); break; case ButtonPress: switch (event.xbutton.button) { case 1: // Left Button LastX = MouseX; LastY = MouseY; break; case 4: // Scrollup ZoomIn(); break; case 5: // Scrolldown ZoomOut(); break; } break; case ButtonRelease: if (popupActive && (event.xbutton.time - lastupevent) < 300) { // Double Click on Station char Key[32]; char LoppedCall[10]; int n = 8; memcpy(LoppedCall, popupStn->Callsign, 9); while (n && LoppedCall[n] == ' ') { n--; } if (n) LoppedCall[n + 1] = 0; sprintf(Key, "|%s|", LoppedCall); // if new call add to combo box if (strstr(ToCalls, Key) == 0) { if (strlen(ToCalls) < 1000) strcat(ToCalls, Key); gtk_combo_box_text_prepend_text ((GtkComboBoxText *)combo, LoppedCall); } } lastupevent = event.xbutton.time; switch (event.xbutton.button) { case 1: // Left Button // if a Popup is on display, then select station, else scroll map if (selActive) { GetStationFromList(MouseX, MouseY); break; } MovedX = MouseX - LastX; MovedY = MouseY - LastY; if (MovedX == 0 && MovedY == 0) break; ScrollX -= (MovedX); ScrollY -= (MovedY); while (ScrollX < 0) { TileX--; ScrollX += 256; } while (ScrollX > 255) { TileX++; ScrollX -= 256; } if (TileX < 0) { TileX = 0; ScrollX = 0; } while (ScrollY < 0) { TileY--; ScrollY += 256; } while (ScrollY > 255) { TileY++; ScrollY -= 256; } if (TileY < 0) { TileY = 0; ScrollY = 0; } NeedRefresh = TRUE; AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs break; } break; } } // end of while xpending if (popupActive || selActive) { } else { if (NeedRefresh) { SetBaseX = -1; LoadImageSet(Zoom, TileX, TileY); NeedRefresh = FALSE; SlowTimer = 0; } if (NeedRedraw) { NeedRedraw = 0; XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); } } } SaveConfig(); status = XDestroyImage (image); return 0; } void PutAPRSMessage(char * Frame, int Len) { if (sendto(sfd, Frame, Len, 0, (struct sockaddr *) &peer_addr, sizeof(struct sockaddr_un)) != Len) perror("sendto"); } VOID SendFilterCommand(char * Filter) { char Msg[2000]; int n; strcpy(Msg, "SERVER"); strcpy(&Msg[10], Filter); PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); strcpy(&Msg[10], "filter?"); PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); } void RefreshTXList() { struct APRSMESSAGE * Message = SMEM->OutstandingMsgs; int n = 0; GtkTreeIter iter; gchar *seq; char status[10]; // Clear old list if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(sentitems), &iter, NULL, 0)) { while (gtk_list_store_remove(sentitems, &iter)) {} } while (Message) { if (Message->Acked) strcpy(status, "A"); else if (Message->Cancelled) strcpy(status, "C"); else if (Message->Retries == 0) strcpy(status, "F"); else sprintf(status, "%d", Message->Retries); gtk_list_store_insert_with_values( sentitems, &iter, -1, 0, Message->ToCall, 1, Message->Seq, 2, status, 3, Message->Time, 4, Message->Text, -1); n++; Message = Message->Next; } if (n) gtk_tree_view_scroll_to_cell ((GtkTreeView *)view, gtk_tree_model_get_path (GTK_TREE_MODEL(sentitems), &iter), NULL, FALSE, 0, 0); } VOID SendAPRSMessage(const char * Text, char * ToCall) { char Msg[255]; strcpy(Msg, ToCall); strcpy(&Msg[10], Text); PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); return; } VOID SecTimer() { while (TRUE) { struct STATIONRECORD * sptr = *StationRecords; int n = 0; char Msg[20]; // See if changed flag set on any stations NeedRedraw += RefreshStationMap(FALSE); // Draw new or moved stations // Do a full redraw at least evey 2 mins if anything has changed SlowTimer++; if (SlowTimer > 120) // 2 Mins if (NeedRedraw) NeedRefresh = TRUE; /* while (sptr) { if (sptr->Moved) DrawStation(sptr, FALSE); sptr = sptr->Next; } */ JPEGCounter++; if (CreateJPEG) { if (JPEGCounter > JPEGInterval) { if (RGBToJpegFile(JPEGFileName, Image, cxImgSize, cyImgSize, TRUE, 50)) JPEGCounter = 0; } } /* if (SendWX) SendWeatherBeacon(); // If any changes to image redraw it if (ImageChanged) { // We have drawn a new Icon. As we only redraw if it has moved, // we need to reload image every now and again to get rid of ghost images time_t NOW = time(NULL); if ((NOW - LastRefresh) > 10) { LastRefresh = NOW; ReloadMaps = TRUE; } ImageChanged = FALSE; InvalidateRect(hMapWnd, NULL, FALSE); } wsprintf(Msg, "%d", StationCount); SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 1, (LPARAM)Msg); wsprintf(Msg, "%d", OSMQueueCount); SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 2, (LPARAM)Msg); wsprintf(Msg, "%d", Zoom); SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 3, (LPARAM)Msg); */ if (AutoFilterTimer) { AutoFilterTimer--; if (AutoFilterTimer == 0 && AddViewToFilter) { // send filter to IS double TLLat, TLLon, BRLat, BRLon; char Filter[256]; GetCornerLatLon(&TLLat, &TLLon, &BRLat, &BRLon); sprintf(Filter, "%s a/%.3f/%.3f/%.3f/%.3f", ISFilter, TLLat, TLLon, BRLat, BRLon); SendFilterCommand(Filter); } } Sleep(1000); } } BOOL RGBToJpegFile(char * fileName, BYTE *dataBuf, UINT widthPix, UINT height, BOOL color, int quality) { struct jpeg_compress_struct cinfo; struct my_error_mgr jerr; FILE * outfile=NULL; unsigned char * RGBBuff = malloc(widthPix * 3); // no idea why unsigned char * ptr1, * ptr2; int n; unsigned int val, r, g, b; char Message[32]; int X, Y, j, Len; struct tm * TM; time_t NOW = time(NULL); if (dataBuf==NULL) return FALSE; if (widthPix==0) return FALSE; if (height==0) return FALSE; // Write a Date/Time stamp to top left if (LocalTime) TM = localtime(&NOW); else TM = gmtime(&NOW); Len = sprintf(Message, "%02d:%02d:%02d %02d %s %04d", TM->tm_hour, TM->tm_min, TM->tm_sec, TM->tm_mday, month[TM->tm_mon], TM->tm_year + 1900); X = ScrollX; Y = ScrollY + 8; for (j = 0; j < Len; j++) { DrawCharacter(X, Y, j, Message[j]); } /* More stuff */ /* Step 1: allocate and initialize JPEG compression object */ cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp(jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_compress(&cinfo); if (outfile!=NULL) fclose(outfile); return FALSE; } /* Now we can initialize the JPEG compression object. */ jpeg_create_compress(&cinfo); /* Step 2: specify data destination (eg, a file) */ /* Note: steps 2 and 3 can be done in either order. */ if ((outfile = fopen(fileName, "wb")) == NULL) { // char buf[250]; // sprintf(buf, "JpegFile :\nCan't open %s\n", fileName); // MessageBox(NULL, buf, "", 0); return FALSE; } jpeg_stdio_dest(&cinfo, outfile); /* Step 3: set parameters for compression */ /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ cinfo.image_width = widthPix; /* image widthPix and height, in pixels */ cinfo.image_height = height; cinfo.input_components = 3; /* # of color components per pixel */ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ /* Now use the library's routine to set default compression parameters. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) */ jpeg_set_defaults(&cinfo); /* Now you can set any non-default parameters you wish to. * Here we just illustrate the use of quality (quantization table) scaling: */ jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); /* Step 4: Start compressor */ /* TRUE ensures that we will write a complete interchange-JPEG file. * Pass TRUE unless you are very sure of what you're doing. */ jpeg_start_compress(&cinfo, TRUE); /* Step 5: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* Here we use the library's state variable cinfo.next_scanline as the * loop counter, so that we don't have to keep track ourselves. * To keep things simple, we pass one scanline per call; you can pass * more if you wish, though. */ while (cinfo.next_scanline < cinfo.image_height) { /* jpeg_write_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could pass * more than one scanline at a time if that's more convenient. */ unsigned char * outRow; outRow = RGBBuff; // We have to convert from 2 or 4 bytes to pixel to 3 byte rgb ptr1 = dataBuf + ((ScrollY * WIDTH) + ScrollX + (cinfo.next_scanline * WIDTH)) * Bytesperpixel; ptr2 = RGBBuff; for (n = 0; n < widthPix; n++) { if (Bytesperpixel == 2) { val = (*(ptr1++)); val |= (*(ptr1++)) << 8; b = val << 3; g = (val >> 5) << 2; r = (val >> 11) << 3; *(ptr2++) = r; *(ptr2++) = g; *(ptr2++) = b; } else { *(ptr2++) = *(ptr1+2); *(ptr2++) = *(ptr1+1); *(ptr2++) = *(ptr1); ptr1 += 4; } } (void) jpeg_write_scanlines(&cinfo, &outRow, 1); } /* Step 6: Finish compression */ jpeg_finish_compress(&cinfo); /* After finish_compress, we can close the output file. */ fclose(outfile); /* Step 7: release JPEG compression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_compress(&cinfo); /* And we're done! */ return TRUE; } VOID plot(int X, int Y, COLORREF rgb) { char * nptr; int i, j; unsigned int val; if ((X > (WIDTH - 3)) || (Y > (HEIGHT - 3))) return; nptr = &Image[(Y * WIDTH * Bytesperpixel) + (X * Bytesperpixel)]; for (j = 0; j < 2; j++) { for (i = 0; i < 2; i++) { if (Bytesperpixel == 4) { *(nptr++) = (rgb >> 16) & 0xff; *(nptr++) = (rgb >> 8) & 0xff; *(nptr++) = GetRValue(rgb); nptr++; } else { val = ((rgb & 0xff) >> 3) << 11; // Red val |= (GetGValue(rgb) >> 2) << 5; val |= (rgb >> 19); // Blue *(nptr++) = (val & 0xff); *(nptr++) = (unsigned char)(val >> 8); } } nptr += (WIDTH - 2) * Bytesperpixel; } } // Algorithm assumes y increases slower than x. If not, swap x and y in plotline and plotpoint void plotLineTB(int x0, int y0, int x1, int y1, COLORREF rgb); void plotLineLR(int x0, int y0, int x1, int y1, COLORREF rgb); void plotLine(int x0, int y0, int x1, int y1, COLORREF rgb) { if (abs(x1 - x0) > abs(y1 - y0)) plotLineLR(x0, y0, x1, y1, rgb); else plotLineTB(y0, x0, y1, x1, rgb); } void plotLineLR(int x0, int y0, int x1, int y1, COLORREF rgb) { int dx; int dy; int D, x, y; // Must draw with increacing x and y, but can draw either way round, so if x is decreasing, // just swap ends, so we always draw left to right if (x0 > x1) { x = x0; x0 = x1; x1 = x; y = y0; y0 = y1; y1 = y; } // if y is now decreasing, we must reverse algorithm if (y1 > y0) { dx = x1 - x0; dy = y1 - y0; D = 2 * dy - dx; plot (x0, y0, 0); y = y0; for (x = x0+1; x < x1; x++) { if (D < 0) { D = D + (2*dy); } else { y = y+1; D = D + (2*dy-2*dx); } plot(x, y, rgb); } } else { dx = x1 - x0; dy = y0 - y1; D = 2 * dy - dx; plot (x0, y0, rgb); y = y0; for (x = x0+1; x <= x1; x++) { if (D > 0) { y = y-1; plot(x, y, rgb); D = D + (2*dy-2*dx); } else { plot(x, y, rgb); D = D + (2*dy); } } } } void plotLineTB(int x0, int y0, int x1, int y1, COLORREF rgb) { int dx; int dy; int D, x, y; // Must draw with increacing x and y, but can draw either way round, so if x is decreasing, // just swap ends, so we always draw left to right if (x0 > x1) { x = x0; x0 = x1; x1 = x; y = y0; y0 = y1; y1 = y; } // if y is now decreasing, we must reverse algorithm if (y1 > y0) { dx = x1 - x0; dy = y1 - y0; D = 2 * dy - dx; plot (y0, x0, 0); y = y0; for (x = x0+1; x < x1; x++) { if (D < 0) { D = D + (2*dy); } else { y = y+1; D = D + (2*dy-2*dx); } plot(y, x, rgb); } } else { dx = x1 - x0; dy = y0 - y1; D = 2 * dy - dx; plot (y0, x0, 0); y = y0; for (x = x0+1; x <= x1; x++) { if (D > 0) { y = y-1; D = D + (2*dy-2*dx); } else { D = D + (2*dy); } plot(y, x, rgb); } } } png_const_charp msg; static png_structp png_ptr = NULL; static png_infop info_ptr = NULL; // cexcept interface static void png_cexcept_error(png_structp png_ptr, png_const_charp msg) { if(png_ptr) ; #ifndef PNG_NO_CONSOLE_IO fprintf(stderr, "libpng error: %s\n", msg); #endif { // Throw msg; } } int LoadImageFile (void * hwnd, char * pstrPathName, png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels, png_color *pBkgColor) { // if there's an existing PNG, free the memory if (*ppbImage) { free (*ppbImage); *ppbImage = NULL; } PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels, pBkgColor); if (*ppbImage != NULL) { // sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1); // SetWindowText (hwnd, szTmp); } else { return FALSE; } return TRUE; } //---------------- // PNG image handler functions BOOL PngLoadImage (char * pstrFileName, png_byte **ppbImageData, png_uint_32 *piWidth, png_uint_32 *piHeight, int *piChannels, png_color *pBkgColor) { static FILE *pfFile; png_byte pbSig[8]; int iBitDepth; int iColorType; double dGamma; png_color_16 *pBackground; png_uint_32 ulChannels; png_uint_32 ulRowBytes; png_byte *pbImageData = *ppbImageData; static png_byte **ppbRowPointers = NULL; int i; // open the PNG input file if (!pstrFileName) { *ppbImageData = pbImageData = NULL; printf("Load PNG Failed 1\n"); return FALSE; } if (!(pfFile = fopen(pstrFileName, "rb"))) { *ppbImageData = pbImageData = NULL; printf("Load PNG Failed 2\n"); return FALSE; } // first check the eight byte PNG signature fread(pbSig, 1, 8, pfFile); if (!png_check_sig(pbSig, 8)) { *ppbImageData = pbImageData = NULL; if (pfFile) fclose (pfFile); printf("Bad file %s", pstrFileName); unlink(pstrFileName); return FALSE; } // create the two png(-info) structures png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL); if (!png_ptr) { *ppbImageData = pbImageData = NULL; printf("Load PNG Failed 4\n"); return FALSE; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); *ppbImageData = pbImageData = NULL; printf("Load PNG Failed 5\n"); return FALSE; } // Try { // initialize the png structure #if !defined(PNG_NO_STDIO) png_init_io(png_ptr, pfFile); #else png_set_read_fn(png_ptr, (png_voidp)pfFile, png_read_data); #endif png_set_sig_bytes(png_ptr, 8); // read all PNG info up to image data png_read_info(png_ptr, info_ptr); // get width, height, bit-depth and color-type png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, &iColorType, NULL, NULL, NULL); // expand images of all color-type and bit-depth to 3x8 bit RGB images // let the library process things like alpha, transparency, background if (iBitDepth == 16) png_set_strip_16(png_ptr); if (iColorType == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (iBitDepth < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (iColorType == PNG_COLOR_TYPE_GRAY || iColorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); // set the background color to draw transparent and alpha images over. if (png_get_bKGD(png_ptr, info_ptr, &pBackground)) { png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); pBkgColor->red = (byte) pBackground->red; pBkgColor->green = (byte) pBackground->green; pBkgColor->blue = (byte) pBackground->blue; } else { pBkgColor = NULL; } // if required set gamma conversion if (png_get_gAMA(png_ptr, info_ptr, &dGamma)) png_set_gamma(png_ptr, (double) 2.2, dGamma); // after the transformations have been registered update info_ptr data png_read_update_info(png_ptr, info_ptr); // get again width, height and the new bit-depth and color-type png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, &iColorType, NULL, NULL, NULL); // row_bytes is the width x number of channels ulRowBytes = png_get_rowbytes(png_ptr, info_ptr); ulChannels = png_get_channels(png_ptr, info_ptr); *piChannels = ulChannels; // now we can allocate memory to store the image if (pbImageData) { free (pbImageData); pbImageData = NULL; } if ((pbImageData = (png_byte *) malloc(ulRowBytes * (*piHeight) * sizeof(png_byte))) == NULL) { png_error(png_ptr, "Visual PNG: out of memory"); } *ppbImageData = pbImageData; // and allocate memory for an array of row-pointers if ((ppbRowPointers = (png_bytepp) malloc((*piHeight) * sizeof(png_bytep))) == NULL) { png_error(png_ptr, "Visual PNG: out of memory"); } // set the individual row-pointers to point at the correct offsets for (i = 0; i < (*piHeight); i++) ppbRowPointers[i] = pbImageData + i * ulRowBytes; // now we can go ahead and just read the whole image png_read_image(png_ptr, ppbRowPointers); // read the additional chunks in the PNG file (not really needed) png_read_end(png_ptr, NULL); // and we're done free (ppbRowPointers); ppbRowPointers = NULL; png_destroy_read_struct(&png_ptr, &info_ptr, NULL); // yepp, done } /* Catch (msg) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); *ppbImageData = pbImageData = NULL; if(ppbRowPointers) free (ppbRowPointers); fclose(pfFile); return FALSE; } */ if (pfFile) fclose (pfFile); return TRUE; }