Other Programming Topics
Timer
MiniGUI application can call function SetTimer to create a timer. When the created timer is expired, the target window will receive message MSG_TIMER as long as the identifier of the timer. When the time is not needed, application program can call KillTimer to delete the timer.
The timer mechanism of MiniGUI provides application with a comparatively convenient timing mechanism. However, the timer mechanism of MiniGUI has the following constraints:
- Each message queue of MiniGUI-Threads only can manage eight timers. Note that when creating a new thread, there will be a new message queue created to correspond, that is, each thread can have maximum eight timers.
- MiniGUI-Processes has only one message queue, which can manage maximum 16 timers.
- The process of timer message is relatively special. Its implementation is similar to the signal mechanism of UNIX operating system. System will ignore a message when a certain timer message has not been processed but new timer message occurs. It is because that if the frequency of certain timer is very high while the window processing this timer responds too slowly, when the message still needs to be posted, the message queue will be finally choked up.
- Timer message has the lowest priority. When there is not other type of messages in the message queue (such as posted message, notification message, and painting message), system will go to check if there is any expired timer.
When the timer frequency is very high, it is possible that such situation like timer message lost or interval asymmetry occurs. If application needs to use relatively accurate timer mechanism, it should use setitimer system call of Linux/UNIX with SIGALRM signal. What needs to note is that the server process (mginit program) of MiniGUI-Processes has called setitimer system call to install the timer. Therefore, the program mginit realized by application itself can not use setitimer to realize the timer. However, the client program of MiniGUI-Processes still can call setitimer. MiniGUI-Threads has no such constraint.
Code in List 12.1 creates a timer with interval of one second, then use current time to set static text when timer is expired so as to display the clock. Finally, program will delete the timer while closing window.
List 12.1 Use of Timer
#define _ID_TIMER 100
#define _ID_TIME_STATIC 100
static char* mk_time (char* buff)
{
time_t t;
struct tm * tm;
time (&t);
tm = localtime (&t);
sprintf (buff, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
return buff;
}
static int TaskBarWinProc (HWND hWnd, int message, WPARAM wParam, LPARAM lParam)
{
char buff [20];
switch (message) {
case MSG_CREATE:
{
CreateWindow (CTRL_STATIC, mk_time (buff),
WS_CHILD | WS_BORDER | WS_VISIBLE | SS_CENTER,
_ID_TIME_STATIC, g_rcExcluded.right - _WIDTH_TIME - _MARGIN, _MARGIN,
_WIDTH_TIME, _HEIGHT_CTRL, hWnd, 0);
/* Create a timer with interval being one second.
* Its identifier is _ID_TIMER, target window is hWnd.
*/
SetTimer (hWnd, _ID_TIMER, 100);
break;
case MSG_TIMER:
{
/* Received a message of MSG_TIMER¡£
* Application should determine whether wParam is _ID_TIMER.
*/
SetDlgItemText (hWnd, _ID_TIME_STATIC, mk_time (buff));
break;
}
case MSG_CLOSE:
/* Delete the timer */
KillTimer (hWnd, _ID_TIMER);
DestroyAllControls (hWnd);
DestroyMainWindow (hWnd);
PostQuitMessage (hWnd);
return 0;
}
return DefaultMainWinProc (hWnd, message, wParam, lParam);
}
It is necessary to explain that the third argument of SetTimer is used to specify the interval of timer. The default unit is 10 milliseconds.
Application can also call ResetTimer to reset interval of a timer. The usage of the function is similar to SetTimer.
Dynamic Change of Color of Window Element
Prior to MiniGUI 1.6.0, color of window element (such as caption bar, border, etc.) is determined by configuration information in MiniGUI.cfg file, and all the windows use the same configuration. In MiniGUI 1.6.0 version, we add the following interfaces, so we can change color (pixel value) of some window elements of a certain window on-the-fly without influencing other windows:
gal_pixel GUIAPI GetWindowElementColorEx (HWND hwnd, Uint16 item); gal_pixel GUIAPI SetWindowElementColorEx (HWND hwnd, Uint16 item, gal_pixel new_value);
The GetWindowElementColorEx function gets the color of a certain window element (specified by item) of a certain window (hwnd); SetWindowElementColorEx sets the color of a certain window element, and returns the old color. Window element is specified by item, and different value of item represents different window element respectively, as shown in Tab. 12.1.
Tab. 12.1 Definitions of window element
| ID for window element | Meanings | Remarks |
| BKC_CAPTION_NORMAL | Background color of caption bar in normal state | - |
| FGC_CAPTION_NORMAL | Foreground color of caption bar in normal state | - |
| BKC_CAPTION_ACTIVED | Background color of caption bar in active state | - |
| FGC_CAPTION_ACTIVED | Foreground color of caption bar in normal state | - |
| BKC_CAPTION_DISABLED | Background color of caption bar in disabled state | - |
| FGC_CAPTION_DISABLED | Foreground color of caption bar in disabled state | - |
| WEC_FRAME_NORMAL | Color of frame in normal state | - |
| WEC_FRAME_ACTIVED | Color of frame in normal state | - |
| WEC_FRAME_DISABLED | Color of frame in disabled state | - |
| WEC_3DBOX_NORMAL | Normal color of 3D-box | Can be used to control the color of 3D-box of control with 3D-display effect such as button. |
| WED_3DBOX_REVERSE | Reversed color of 3D-box | |
| WEC_3DBOX_LIGHT | Highlight color of 3D-box | |
| WEC_3DBOX_DARK | Dark color of 3D-box | |
| WEC_FLAT_BORDER | Border color of flat box | - |
| FGC_CONTROL_DISABLED | Foreground color of flat box | Can be used to control the color of controls with selection property such as edit box, list box etc. |
| BKC_HILIGHT_NORMAL | Background color of section selected by control | |
| BKC_HILIGHT_LOSTFOCUS | Background color of section selected by control when lose focus | |
| FGC_HILIGHT_NORMAL | Foreground color of section selected by control | |
| BKC_CONTROL_DEF | Background color of control by default | Used to control the color of foreground and background of normal control |
| FGC_CONTROL_NORMAL | Foreground color of control in normal state | |
| FGC_HILIGHT_DISABLED | Foreground color of section selected by control in disabled state | - |
| BKC_DESKTOP | Background color of desktop | Global property |
| BKC_DIALOG | Background color by default of dialog box |
When you needs to change the painting color of some element of a window (main window or control), you can write code as follows:
HWND hwnd = GetDlgItem (hDlg, IDC_STATIC); gal_pixel pixel = RGB2Pixel (HDC_SCREEN, r, g, b); SetWindowElementColorEx (hwnd, FGC_CONTROL_NORMAL, pixel); UpdateWindow (hwnd, TRUE);
The code above changes the foreground color of a static box in dialog box. It should be noted that if the window is visible, then you should call UpdateWindow function to update the window to reflect the change.
Note that if you want to change the background color of main window of control, you should use SetWindowBkColor function, for example:
HWND hwnd = GetDlgItem (hDlg, IDC_STATIC); gal_pixel pixel = RGB2Pixel (HDC_SCREEN, r, g, b); SetWindowBkColor (hwnd, pixel); InvalidateRect (hwnd, NULL, TRUE);
Clipboard
Clipboard is a tool to transfer data, and can be used for data communication among applications and application internals. Its principle is very simple, that is, a program places data on the clipboard, and another application takes the data from the clipboard. Clipboard is a data interchange station among applications.
Edit box control in MiniGUI supports clipboard operations, when the user selects text and press down
MiniGUI provides a default text clipboard, named as CBNAME_TEXT (string name "text"), used for copying and pasting of text. Applications can use the clipboard directly without other additional operations. A clipboard defined by an application itself need to be created with CreateClipBoard function, and to be destroyed with DestroyClipBoard after using it.
There are at most NR_CLIPBOARDS clipboards in MiniGUI, including the system default text clipboard and clipboards defined by user. NR_CLIPBOARDS macro is defined to be 4 in minigui/window.h header file by default.
CreateClipBoard function creates a clipboard with specified name, this name can not be same as the name of existing clipboards (system defined or user defined):
int GUIAPI CreateClipBoard (const char* cb_name, size_t size);
Argument cb_name specifies the name of a clipboard, argument size specifies the size of data to be stored by the clipboard. If creates successfully, the function returns CBERR_OK; if the name is reduplicate, the function returns CBERR_BADNAME; if the memory is not enough, the function returns CBERR_NOMEM.
The DestroyClipBoard function destroys a user defined clipboard created by CreateClipBoard function:
int GUIAPI DestroyClipBoard (const char* cb_name);
SetClipBoardData function transfers data to a specified clipboard:
int GUIAPI SetClipBoardData (const char* cb_name, void* data, size_t n, int cbop);
Here, the argument cb_name specifies the name of clipboard; data is the pointer to data buffer; n is the size of data; cbop is the operation type, which can be:
- CBOP_NORMAL: Default overlap operation. The new data overlaps the existing data on the clipboard;
- CBOP_APPEND: Append operation. The new data will be appended to the existing data on the clipboard
GetClipBoardDataLen function is used to get the size of data on clipboard:
size_t GUIAPI GetClipBoardDataLen (const char* cb_name);
GetClipBoardData function is used to copy the data on clipboard to the specified data buffer:
size_t GUIAPI GetClipBoardData (const char* cb_name, void* data, size_t n);
Here the argument cb_name specifies the name of the clipboard; data is the pointer to the data buffer; n specifies the size of specified data buffer. The function returns the size of gotten data from the clipboard
Generally speaking, you can use GetClipBoardDataLen function to get the size of data before using GetClipBoardData function to get data of clipboard, so that you can specify an appropriate data buffer to save data.
GetClipBoardByte function is used to get a byte from the specified position of data on clipboard.
int GUIAPI GetClipBoardByte (const char* cb_name, int index, unsigned char* byte);
Here the argument index specifies the index position of specified data, byte is used to save the gotten byte data.
Reading/Writing Configuration File
The configuration file of MiniGUI (default as /usr/local/etc/MiniGUI.cfg file) uses Windows INI-like file format. This format is very simple, seen as follows:
[section-name1] key-name1=key-value1 key-name2=key-value2 [section-name2] key-name3=key-value3 key-name4=key-value4
The information of such configuration file is grouped by section, then uses key=value format to appoint parameter and its value. Application can also use this format to store some configuration information. So, MiniGUI provides following functions (minigui/minigui.h):
int GUIAPI GetValueFromEtcFile (const char* pEtcFile, const char* pSection,
const char* pKey, char* pValue, int iLen);
int GUIAPI GetIntValueFromEtcFile (const char* pEtcFile, const char* pSection,
const char* pKey, int* value);
int GUIAPI SetValueToEtcFile (const char* pEtcFile, const char* pSection,
const char* pKey, char* pValue);
GHANDLE GUIAPI LoadEtcFile (const char* pEtcFile);
int GUIAPI UnloadEtcFile (GHANDLE hEtc);
int GUIAPI GetValueFromEtc (GHANDLE hEtc, const char* pSection,
const char* pKey, char* pValue, int iLen);
int GUIAPI GetIntValueFromEtc (GHANDLE hEtc, const char* pSection,
const char* pKey, int *value);
int GUIAPI SetValueToEtc (GHANDLE hEtc, const char* pSection,
const char* pKey, char* pValue);
The use of first three functions are as follow:
- GetValueFromEtcFile: To get a specified key value from a specified configuration file. The value of the key returns as a string.
- GetIntValueFromEtcFile: To get a specified key integer value from a specified configuration file. This function converts the accepted string to integer value (using strtol function) and then returns the value.
- SetValueToEtcFile: This function stores the given key value into a specified configuration file. If the configuration file does not exist, this function will create new configuration file. If the file exists, the old value will be covered.
The last five functions are new configuration file reading/writing function since MiniGUI version 1.5.x; the use of them is as follows:
- LoadEtcFile: Read a specified configuration file into memory and returns a configuration object handle, then the related functions can visit the configuration information in memory through this handle.
- UnloadEtcFile: Release the configuration information in memory.
- GetValueFromEtc: The way of use is similar to GetValueFromEtcFile; But its first argument is the configuration object handle, not the file name. This function can be used to get configuration information from memory.
- GetIntValueFromEtc: The way of use is similar to GetIntValueFromEtcFile.
- SetValueToEtc: Similar to SetValueToEtcFile, but this function only changes the configuration key value in memory, does not affect the content in the file.
These functions are usually used to read all information of configuration file for once. When need to get relatively more key values, this function will first use LoadEtcFile to read in a configuration file, then use GetValueFromEtc to get key value. When there is no need to visit configuration information, use UnloadEtcFile to release the configuration object handle.
Assuming that certain configuration file records some application information, and have following formats:
[mginit] nr=8 autostart=0 [app0] path=../tools/ name=vcongui layer= tip=Virtual&console&on&MiniGUI icon=res/konsole.gif [app1] path=../bomb/ name=bomb layer= tip=Game&of&Minesweaper icon=res/kmines.gif [app2] path=../controlpanel/ name=controlpanel layer= tip=Control&Panel icon=res/kcmx.gif
The section [mginit] records the number of applications and its automatically-startup index (autostart key). The section [appX] records information of each application program, including the path, the name, and the icon of the application. Code in List 12.2 illustrate how to use the functions above to get such information (the code comes from program mginit of MDE).
List 12.2 Using MiniGUI configuration file functions to get information
#define APP_INFO_FILE "mginit.rc"
static BOOL get_app_info (void)
{
int i;
APPITEM* item;
/* Get information of the number of programs */
if (GetIntValueFromEtcFile (APP_INFO_FILE, "mginit", "nr", &app_info.nr_apps) != ETC_OK)
return FALSE;
if (app_info.nr_apps <= 0)
return FALSE;
/* Get index of autostarting application */
GetIntValueFromEtcFile (APP_INFO_FILE, "mginit", "autostart", &app_info.autostart);
if (app_info.autostart >= app_info.nr_apps || app_info.autostart < 0)
app_info.autostart = 0;
/* Calloc inforamtion structure of application */
if ((app_info.app_items = (APPITEM*)calloc (app_info.nr_apps, sizeof (APPITEM))) == NULL) {
return FALSE;
}
/* Get information of each application such as path, name and icon*/
item = app_info.app_items;
for (i = 0; i < app_info.nr_apps; i++, item++) {
char section [10];
sprintf (section, "app%d", i);
if (GetValueFromEtcFile (APP_INFO_FILE, section, "path",
item->path, PATH_MAX) != ETC_OK)
goto error;
if (GetValueFromEtcFile (APP_INFO_FILE, section, "name",
item->name, NAME_MAX) != ETC_OK)
goto error;
if (GetValueFromEtcFile (APP_INFO_FILE, section, "layer",
item->layer, LEN_LAYER_NAME) != ETC_OK)
goto error;
if (GetValueFromEtcFile (APP_INFO_FILE, section, "tip",
item->tip, TIP_MAX) != ETC_OK)
goto error;
strsubchr (item->tip, '&', ' ');
if (GetValueFromEtcFile (APP_INFO_FILE, section, "icon",
item->bmp_path, PATH_MAX + NAME_MAX) != ETC_OK)
goto error;
if (LoadBitmap (HDC_SCREEN, &item->bmp, item->bmp_path) != ERR_BMP_OK)
goto error;
item->cdpath = TRUE;
}
return TRUE;
error:
free_app_info ();
return FALSE;
}
If using LoadEtcFile, GetValueFromEtc, and UnloadEtcFile to implement above example, the code will be as the following:
GHANDLE hAppInfo; HAppInfo = LoadEtcFile (APP_INFO_FILE); //¡ get_app_info (); //¡ UnloadEtcFile (hAppInfo);
We also need change GetValueFromEtcFile of function get_app_info to GetValueFromEtc.
Writing Portable Program
As we know, the CPU used by most embedded system has totally different construction and characteristic from the CPU of normal desktop PC. However, operating system and advanced language can hide these differences to a great extent. With the support of advanced language programming, the compiler and operating system can help programrs solve most problems related to CPU architecture and characteristic in order to save developing time and increase developing efficiency. However, application programrs have to face some certain CPU characteristics; the following aspects need to be paid more attention:
- The order of byte. Generally, when CPU stores integer data of multi-bytes, it will store the low-bit byte in low address unit, such as Intel x86 series. Some CPU uses opposite order to store. For example, the popularly used PowerPC in embedded system stores low-bit byte in high address unit. The former is called little-endian system while the latter is called big-endian system.
- The Linux kernel on some platforms may lack of some advanced system calls, the most popular one is the system calls related to virtual memory mechanism. The Linux system running on certain CPU cannot provide virtual memory mechanism because of the limitation of CPU capability. For example, CPU lack of MMU unit cannot provide the sharing memory of System V IPC mechanism.
In order to write the portable code having most popular adaptability, application programrs must notice these differences and write code according to different situations. Here we will describe how to write portable code in MiniGUI applications.
In order to solve the first problem mentioned above, MiniGUI provides several endian-related read/write functions. These functions can be divided into two categories:
- Functions used to swap the order of byte, including ArchSwapLE16, ArchSwapBE16 and so on.
- Functions used to read/write standard I/O stream, including MGUI_ReadLE16, MGUI_ReadBE16 and so on.
The first category is used to convert the 16-bit, 32-bit, or 64-bit integer into system native byte from certain byte order. For example:
int fd, len_header;
...
if (read (fd, &len_header, sizeof (int)) == -1)
goto error;
#if MGUI_BYTEORDER == MGUI_BIG_ENDIAN
len_header = ArchSwap32 (len_header); // If it is big-endian system, swap the order
#endif
...
The above code first uses read system call to read an integer value from the a specified file descriptor to variable len_header. The integer value saved in this file is in little-endian, so the byte order of this integer value has to be swapped if this integer value is used in big-endian system. We can use ArchSwapLE32 to convert 32-bit integer value into system native byte order. Also, we can swap the bytes only for big-endian system, and then we just need to use ArchSwap32 function.
The functions (or macro) used to swap bytes are as follow:
- ArchSwapLE16(X) converts the specified 16-bit integer value (stored in little endian byte order) to system native integer value. If system is little-endian, this function will directly return X; if system is big-endian, the function will call ArchSwap16 to swap the bytes.
- ArchSwapLE32(X) converts the specified 32-bit integer value (stored in little endian byte order) to system native integer value. If system is little endian, this function will directly return X; if system is big endian, the function will call ArchSwap32 to swap the bytes.
- ArchSwapBE16(X) converts the specified 16-bit integer value (stored in big endian byte order) to system native integer value. If system is big endian, this function will directly return X; if system is little endian, the function will call ArchSwap16 to swap the bytes.
- ArchSwapBE32(X) converts the specified 32-bit integer value (stored in big endian byte order) to system native integer value. If system is big endian, this function will directly return X; if system is little endian, the function will call ArchSwap32 to swap the bytes.
The second category of functions provided by MiniGUI is used to read/write integer value from standard I/O file object. If the file is stored in little endian byte order, the function uses MGUI_ReadLE16 and MGUI_ReadLE32 to read integer value by converting integer value to system native byte order, whereas uses MGUI_ReadBE16 and MGUI_ReadBE32. If the file is stored as little endian byte order, the function will use MGUI_WriteLE16 and MGUI_WriteLE32 to write integer value after converting integer value from system native byte order to little endian; whereas use MGUI_WriteBE16 and MGUI_WriteBE32. The following code explain the above functions:
FILE* out;
int count;
...
MGUI_WriteLE32 (out, count); // Write count to the file in little endian
When regarding problems related to portability, we can easily use the way described above to perform function wrap in order to provide well portable code. However, sometime we cannot use such way to provide the portable code, so we can only use conditional compilation. Code in List 12.3 illustrates how to use conditional compilation to ensure the program running well (the code come from MiniGUI src/kernel/sharedres.c).
List 12.3 The use of conditional compilation
/* If system does not support memory share, define _USE_MMAP */
#undef _USE_MMAP
/* #define _USE_MMAP 1 */
void *LoadSharedResource (void)
{
#ifndef _USE_MMAP
key_t shm_key;
void *memptr;
int shmid;
#endif
/* Load share resource*/
...
#ifndef _USE_MMAP /* Get object of share memory*/
if ((shm_key = get_shm_key ()) == -1) {
goto error;
}
shmid = shmget (shm_key, mgSizeRes, SHM_PARAM | IPC_CREAT | IPC_EXCL);
if (shmid == -1) {
goto error;
}
// Attach to the share memory.
memptr = shmat (shmid, 0, 0);
if (memptr == (char*)-1)
goto error;
else {
memcpy (memptr, mgSharedRes, mgSizeRes);
free (mgSharedRes);
}
if (shmctl (shmid, IPC_RMID, NULL) < 0)
goto error;
#endif
/* Open a file */
if ((lockfd = open (LOCKFILE, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1)
goto error;
#ifdef _USE_MMAP
/* If use mmap, write share resource into the file*/
if (write (lockfd, mgSharedRes, mgSizeRes) < mgSizeRes)
goto error;
else
{
free(mgSharedRes);
mgSharedRes = mmap( 0, mgSizeRes, PROT_READ|PROT_WRITE, MAP_SHARED, lockfd, 0);
}
#else
/* otherwise write the object ID of share memory into the file*/
if (write (lockfd, &shmid, sizeof (shmid)) < sizeof (shmid))
goto error;
#endif
close (lockfd);
#ifndef _USE_MMAP
mgSharedRes = memptr;
SHAREDRES_SHMID = shmid;
#endif
SHAREDRES_SEMID = semid;
return mgSharedRes;
error:
perror ("LoadSharedResource");
return NULL;
}
The above code fragment is used by the MiniGUI-Processes server program to load sharing resource. If system supports shared memory, it will initialize the shared memory object and associate the shared resource with the shared memory object, then write the shared memory object ID into a file; if system does not support shared memory, it will write all initialized sharing resource into a file. If the system support shared memory, clients can get shared memory object ID from the file and directly attach it; if the system does not support shared memory, clients can use mmap system call to map the file to the address space of them. Code of clients can be seen in List 12.4.
List 12.4 The use of conditional compilation (cont.)
void* AttachSharedResource (void)
{
#ifndef _USE_MMAP
int shmid;
#endif
int lockfd;
void* memptr;
if ((lockfd = open (LOCKFILE, O_RDONLY)) == -1)
goto error;
#ifdef _USE_MMAP
/* Use mmap to image share resource to process address space */
mgSizeRes = lseek (lockfd, 0, SEEK_END );
memptr = mmap( 0, mgSizeRes, PROT_READ, MAP_SHARED, lockfd, 0);
#else
/* Otherwise get ID of the object of share memroy, and associate the share memory */
if (read (lockfd, &shmid, sizeof (shmid)) < sizeof (shmid))
goto error;
close (lockfd);
memptr = shmat (shmid, 0, SHM_RDONLY);
#endif
if (memptr == (char*)-1)
goto error;
return memptr;
error:
perror ("AttachSharedResource");
return NULL;
}
Fixed-Point Computing
Usually when we perform math computes, we will use float-point to represent real number, and use
- Conversion among integer, float-point number and fixed-point number. Itofix converts integer to fixed-point number, while fixtoi converts fixed-point to integer; ftofix converts float-point number to fixed-point number, while fixtof converts fixed-point number to float-point number.
- The basic arithmetic computing such as add, subtract, multiple, and divide of fixed-point numbers: fadd, fsub, fmul, fdiv, fsqrt.
- The triangle compute of fixed-point number: fcos, fsin, ftan, facos, fasin.
- Matrix and vector computing. Matrix and vector related computing are important for three-dimension graphics. Readers can refer to minigui/fixedmath.h for the functions
Code in List 12.5 illustrates the use of fixed-point number. This code fragment draws a circle arc according to three given points: pts[0], pts[1], and pts[2]. Pts[0] is the center of the arc, pts[1] is the starting point of the arc, and pts[2] is a point of the line between the end point of arc and center of circle.
List 12.5 Fixed-point computing
void draw_arc (HDC hdc, POINT* pts)
{
int sx = pts [0].x, sy = pts [0].y;
int dx = pts [1].x - sx, dy = pts [1].y - sy;
int r = sqrt (dx * dx * 1.0 + dy * dy * 1.0);
double cos_d = dx * 1.0 / r;
fixed cos_f = ftofix (cos_d);
fixed ang1 = facos (cos_f);
int r2;
fixed ang2;
if (dy > 0) {
ang1 = fsub (0, ang1);
}
dx = pts [2].x - sx;
dy = pts [2].y - sy;
r2 = sqrt (dx * dx * 1.0 + dy * dy * 1.0);
cos_d = dx * 1.0 / r2;
cos_f = ftofix (cos_d);
ang2 = facos (cos_f);
if (dy > 0) {
ang2 = fsub (0, ang2);
}
Arc (hdc, sx, sy, r, ang1, ang2);
}
The calculation of above program is very simple. The steps are as follow (the code comes from painter/painter.c program of MDE):
- Calculate the radius of arc according to pts[0] and pts[1], then use ftofix function and facos function to calculate the starting slanting angel, that is, ang1.
- Calculate the nip angel between pts[2] point and the connection line of the center of circle, also used ftofix function and facos function.
- Call Arc function to draw the arc.