linbpq/XAprsCopy.c

4464 lines
104 KiB
C
Raw Blame History

/*
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <X11/Xlib.h>
#include <X11/X.h>
#define XK_MISCELLANY
#include <X11/keysymdef.h>
#include <setjmp.h>
#include </usr/include/jpeglib.h>
#include <unistd.h>
#include <sys/mman.h>
#define LIBCONFIG_STATIC
#include "libconfig.h"
#include <gtk/gtk.h>
#include <gtk/gtkadjustment.h>
#include <gtk/gtkwidget.h>
#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;
}
/*
<td>Rain last hour</td><td>##RAIN_HOUR_IN##"</td></tr>
<tr><td>Rain today</td><td>##RAIN_TODAY_IN##"</td></tr>
<tr><td>Rain last 24 hours</td><td>##RAIN_24_IN##"</td></tr>
</table>
*/
}
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;count<widthPix;count++) {
BYTE iGray;
// get our grayscale value
iGray = *(jpegline + count);
*(outBuf + offset + count * 3 + 0) = iGray;
*(outBuf + offset + count * 3 + 1) = iGray;
*(outBuf + offset + count * 3 + 2) = iGray;
}
}
//
// read a JPEG file
//
BYTE * JpegFileToRGB(char * fileName, UINT *width, UINT *height, int XOffset, int YOffset)
{
/* This struct contains the JPEG decompression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
*/
struct jpeg_decompress_struct cinfo;
/* We use our private extension JPEG error handler.
* Note that this struct must live as long as the main JPEG parameter
* struct, to avoid dangling-pointer problems.
*/
struct my_error_mgr jmerr;
struct jpeg_error_mgr jerr;
/* More stuff */
FILE * infile=NULL; /* source file */
JSAMPARRAY buffer; /* Output row buffer */
int row_stride; /* physical row width in output buffer */
char buf[250];
// BYTE *dataBuf;
// basic code from IJG Jpeg Code v6 example.c
*width=0;
*height=0;
/* In this example we want to open the input file before doing anything else,
* so that the setjmp() error recovery below can assume the file is open.
* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
* requires it in order to read binary files.
*/
if ((infile = fopen(fileName, "rb")) == NULL) {
return NULL;
}
cinfo.err = jpeg_std_error(&jerr);
/* Step 1: allocate and initialize JPEG decompression object */
/* We set up the normal JPEG error routines, then override error_exit. */
// 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(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;
}
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(&cinfo);
/* Step 2: specify data source (eg, a file) */
jpeg_stdio_src(&cinfo, infile);
/* Step 3: read file parameters with jpeg_read_header() */
(void) jpeg_read_header(&cinfo, TRUE);
/* We can ignore the return value from jpeg_read_header since
* (a) suspension is not possible with the stdio data source, and
* (b) we passed TRUE to reject a tables-only JPEG file as an error.
* See libjpeg.doc for more info.
*/
/* Step 4: set parameters for decompression */
/* In this example, we don't need to change any of the defaults set by
* jpeg_read_header(), so we do nothing here.
*/
/* Step 5: Start decompressor */
(void) jpeg_start_decompress(&cinfo);
/* We can ignore the return value since suspension is not possible
* with the stdio data source.
*/
/* We may need to do some setup of our own at this point before reading
* the data. After jpeg_start_decompress() we have the correct scaled
* output image dimensions available, as well as the output colormap
* if we asked for color quantization.
* In this example, we need to make an output work buffer of the right size.
*/
// get our buffer set to hold data
////////////////////////////////////////////////////////////
// alloc and open our new buffer
// dataBuf = malloc(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) {
/* 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 <20> 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;
}