Subversion Repositories Kolibri OS

Rev

Rev 8553 | Rev 8555 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /* Copyright (C) 2019-2021 Logaev Maxim (turbocat2001), GPLv2 */
  2.  
  3. /*
  4.     Info: App uses api from openweathermap.org.
  5.     The standard configuration uses my token and the city of Moscow.
  6.     You can always change it in the weather.json file.
  7.     If you use UTF-8 encoding, then city names can be entered in different languages!
  8. */
  9.  
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include "json/json.h"
  13. #include <kos32sys1.h>
  14. #include <kolibrisys.h>
  15. #include <clayer/http.h>
  16. #include <clayer/libimg.h>
  17.  
  18. #define VERSION  "Weather 1.2b"
  19.  
  20. enum BUTTONS{
  21.     BTN_QUIT = 1,
  22.     BTN_UPDATE = 2
  23. };
  24.  
  25. #define JSON_OBJ(X) value->u.object.values[X]
  26. #define OK 200
  27. #define API       "http://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=metric"
  28. #define IMAGE_URL "http://openweathermap.org/img/w/%s.png"
  29. #define START_YPOS 34
  30. #define UTF8_W 8
  31. #define CP866_W 6
  32.  
  33. #define WINDOW_W 200
  34.  
  35. Image *image;
  36. Image *blend;
  37.  
  38. char full_url[512];
  39. char full_url_image[256];
  40. struct kolibri_system_colors sys_color_table;
  41.  
  42. pos_t win_pos;
  43.  
  44. #pragma pack(push,1)
  45. typedef struct {
  46.     char City[256];
  47.     int wind_speed;
  48.     //int wind_deg;
  49.     int pressure;
  50.     int humidity;
  51.     //char weath_main[256];
  52.     char weath_desc[256];
  53.     int visibility;
  54.     int timezone;
  55.     char image_code[4];
  56.     int temp;
  57. }open_weather_data;
  58. #pragma pack(pop)
  59.  
  60. open_weather_data myw;
  61.  
  62. void notify_show(char *text)
  63. {
  64.    start_app("/sys/@notify", text);
  65. }
  66.  
  67. void* safe_malloc(size_t size) // Безопасный malloc. Показывает уведомление об ошибке и закрывает программу если память не была выделена
  68. {
  69.     void *p=malloc(size);
  70.     if(p==NULL){
  71.        notify_show("'Memory allocation error!' -E");
  72.        exit(0);
  73.     }else{
  74.         return p;
  75.     }
  76. }
  77.  
  78.  char tmp_buff[100];
  79.  
  80. static void draw_format_text_sys(int x, int y, color_t color, const char *format_str, ... )
  81. {
  82.     va_list ap;
  83.     va_start (ap, format_str);
  84.     vsnprintf(tmp_buff, sizeof tmp_buff ,format_str, ap);
  85.     va_end(ap);
  86.     draw_text_sys(tmp_buff, x, y , 0, color);
  87. }
  88.  
  89. void find_and_set(json_value *value, open_weather_data* weather)
  90. {
  91.     for(int i=0; i<value->u.object.length; i++){
  92.         if(!strcmp(JSON_OBJ(i).name, "main")){
  93.             if(JSON_OBJ(i).value->u.object.values[0].value->type==json_double)
  94.             {
  95.                 weather->temp = (int)JSON_OBJ(i).value->u.object.values[0].value->u.dbl;
  96.             }else{
  97.                 weather->temp = JSON_OBJ(i).value->u.object.values[0].value->u.integer;
  98.             }
  99.             weather->pressure = JSON_OBJ(i).value->u.object.values[4].value->u.integer;
  100.             weather->humidity = JSON_OBJ(i).value->u.object.values[5].value->u.integer;
  101.         }
  102.         if(!strcmp(JSON_OBJ(i).name, "name")){
  103.             strcpy(weather->City,JSON_OBJ(i).value->u.string.ptr);
  104.         }
  105.         if(!strcmp(JSON_OBJ(i).name, "weather")){
  106.            strcpy(weather->weath_desc, JSON_OBJ(i).value->u.array.values[0]->u.object.values[2].value->u.string.ptr);
  107.            strcpy(weather->image_code, JSON_OBJ(i).value->u.array.values[0]->u.object.values[3].value->u.string.ptr);
  108.         }
  109.         if(!strcmp(JSON_OBJ(i).name, "wind")){
  110.             if(JSON_OBJ(i).value->u.object.values[0].value->type==json_double)
  111.             {
  112.                 weather->wind_speed = (int)JSON_OBJ(i).value->u.object.values[0].value->u.dbl;
  113.             }else{
  114.                 weather->wind_speed = JSON_OBJ(i).value->u.object.values[0].value->u.integer;
  115.             }  
  116.         }
  117.         if(!strcmp(JSON_OBJ(i).name, "visibility")){
  118.             weather->visibility = JSON_OBJ(i).value->u.integer;
  119.         }
  120.         if(!strcmp(JSON_OBJ(i).name, "timezone")){
  121.             weather->timezone = JSON_OBJ(i).value->u.integer/60/60;
  122.         }
  123.         if(!strcmp(JSON_OBJ(i).name, "message")){
  124.             char *errmsg = safe_malloc(weather->timezone = JSON_OBJ(i).value->u.string.length+6);
  125.             sprintf(errmsg,"'%s!' -E", JSON_OBJ(i).value->u.string.ptr);
  126.             notify_show(errmsg);
  127.             free(errmsg);
  128.         }
  129.     }
  130. }
  131.  
  132. http_msg* get_json(char *City, char *Token)
  133. {
  134.     sprintf(full_url, API, City, Token);
  135.     http_msg *h = http_get(full_url, 0,  HTTP_FLAG_BLOCK, "");
  136.     http_long_receive(h);
  137.     if (h->status == OK || h->status == 404) {
  138.         return h;
  139.     } else {
  140.         return NULL;
  141.     }
  142. }
  143.  
  144. void get_image(){
  145.     sprintf(full_url_image, IMAGE_URL, myw.image_code);
  146.     http_msg *h= http_get(full_url_image, 0,  HTTP_FLAG_BLOCK, "");
  147.     http_long_receive(h);
  148.    
  149.     if (h->status == OK) {
  150.         image = img_decode(h->content_ptr, h->content_length, 0); // Decode RAW data to Image data
  151.         if (image->Type != IMAGE_BPP32) {
  152.             image = img_convert(image, NULL, IMAGE_BPP32, 0, 0); // Convert image to format BPP32
  153.                 if (!image) {
  154.                 notify_show("'Convetring image error!' -E");  
  155.                 exit(0);
  156.             }
  157.         }
  158.         user_free(h->content_ptr);
  159.         user_free(h);
  160.         blend = img_create(64, 64, IMAGE_BPP32);  // Create an empty layer
  161.         img_fill_color(blend, 64, 64, sys_color_table.work_area); // Fill the layer with one color
  162.         Image* image2 = img_scale(image, 0, 0, 50, 50, NULL, LIBIMG_SCALE_STRETCH , LIBIMG_INTER_BILINEAR, 64, 64);
  163.         img_blend(blend, image2, 0, 0, 0, 0, 64, 64);  // Blending images to display the alpha channel.
  164.         img_destroy(image);
  165.         img_destroy(image2);
  166.     }else{
  167.        notify_show("'Image not loaded!!' -W");
  168.     }  
  169. }
  170.  
  171. void RedrawGUI() //Рисуем окно
  172. {
  173.     begin_draw(); //Начинаем рисование интерфейса )
  174.    
  175.     int new_win_w = (strlen(myw.City)+11)*UTF8_W;
  176.     if(new_win_w<WINDOW_W){
  177.         new_win_w=WINDOW_W;
  178.     }
  179.    
  180.     sys_create_window(win_pos.x, win_pos.y, new_win_w, START_YPOS+200, VERSION, sys_color_table.work_area, 0x14); // Создаём окно.
  181.  
  182.     draw_format_text_sys(20, START_YPOS, 0xB0000000 | sys_color_table.work_text, "%s (UTC%+d)", myw.City, myw.timezone);
  183.     draw_format_text_sys(21, START_YPOS, 0xB0000000 | sys_color_table.work_text, "%s (UTC%+d)", myw.City, myw.timezone);
  184.  
  185.     img_draw(blend, 10, START_YPOS+30, 64,64,0,0);
  186.  
  187.     draw_format_text_sys(20, START_YPOS+20, 0xb0000000 | sys_color_table.work_text, myw.weath_desc);
  188.     draw_format_text_sys(21, START_YPOS+20, 0xb0000000 | sys_color_table.work_text, myw.weath_desc);
  189.  
  190.     draw_format_text_sys(100, START_YPOS+45, 0xB1000000 | sys_color_table.work_text, "%+d°C", myw.temp);  
  191.     draw_format_text_sys(101, START_YPOS+46, 0xB1000000 | sys_color_table.work_text, "%+d°C", myw.temp);
  192.  
  193.     draw_format_text_sys(20, START_YPOS+80, 0x90000000 | sys_color_table.work_text,  "Pressure:   %d hPa",myw.pressure);
  194.     draw_format_text_sys(20, START_YPOS+100, 0x90000000 | sys_color_table.work_text, "Humidity:   %d %s", myw.humidity, "%");
  195.     draw_format_text_sys(20, START_YPOS+120, 0x90000000 | sys_color_table.work_text, "Wind speed: %d m/s", myw.wind_speed);
  196.     draw_format_text_sys(20, START_YPOS+140, 0x90000000 | sys_color_table.work_text, "Visibility: %d m", myw.visibility);
  197.    
  198.     define_button(X_W(new_win_w/2-40,80), Y_H(START_YPOS+160,30), BTN_UPDATE, sys_color_table.work_button);
  199.     draw_text_sys("Update", (new_win_w/2)-(CP866_W*4), START_YPOS+170, 0, 0x90000000 | sys_color_table.work_button_text);
  200.     end_draw();
  201. }
  202.  
  203. void get_config(char **City, char **Token)
  204. {
  205.     FILE *config_j = fopen("weather.json", "rb");
  206.     if(config_j==NULL){
  207.         notify_show("'Configuration file not found!' -E");
  208.         exit(0);
  209.     }
  210.     size_t size = _ksys_get_filesize("weather.json");
  211.     char *config_buff = safe_malloc(size+1);
  212.     if(size != fread(config_buff, sizeof(char), size, config_j)){
  213.         notify_show("'The configuration file was not fully read!' -E");
  214.         exit(0);    
  215.     }
  216.     json_value* value =json_parse (config_buff, size);
  217.     for(int i=0; i<value->u.object.length; i++){
  218.         if(!strcmp(JSON_OBJ(i).name, "Location")){  
  219.           *City = JSON_OBJ(i).value->u.string.ptr;
  220.         }
  221.         if(!strcmp(JSON_OBJ(i).name, "Token")){
  222.            *Token = JSON_OBJ(i).value->u.string.ptr;
  223.         }
  224.     }
  225.     if(*City==NULL || *Token ==NULL){
  226.          notify_show("'Invalid config!!' -E");
  227.          exit(0);
  228.     }
  229.     free(config_buff);
  230.     fclose(config_j);
  231. }
  232.  
  233. void Update(char* city, char* token)
  234. {
  235.     if(blend!=NULL){
  236.         img_destroy(blend);
  237.     }
  238.     memset(&myw, 0, sizeof myw);
  239.     strcpy(myw.City,"None");
  240.     strcpy(myw.weath_desc,"unknown");
  241.     http_msg *json_file = get_json(city, token);
  242.     if(json_file != NULL){
  243.         json_value* value=json_parse (json_file->content_ptr, json_file->content_length);
  244.         find_and_set(value, &myw);
  245.         get_image();
  246.         json_value_free(value);
  247.         user_free(json_file->content_ptr);
  248.         user_free(json_file);
  249.  
  250.     }else{
  251.        notify_show("'Connection error!' -E");
  252.     }
  253. }
  254.  
  255. int main(){
  256.     win_pos = get_mouse_pos(0);
  257.     if(!kolibri_libimg_init()){
  258.         notify_show("Libimg.obj not loaded!' -E");
  259.         exit(0);
  260.     }
  261.     get_system_colors(&sys_color_table);
  262.     char *City = NULL;
  263.     char *Token = NULL;
  264.     get_config(&City, &Token);
  265.     Update(City,Token);
  266.  
  267.     while(1){
  268.         switch(get_os_event()){
  269.             case KOLIBRI_EVENT_NONE:
  270.                 break;
  271.             case KOLIBRI_EVENT_REDRAW:
  272.                 RedrawGUI();
  273.                 break;        
  274.             case KOLIBRI_EVENT_BUTTON: // Событие обработки кнопок
  275.                 switch (get_os_button()){
  276.                     case BTN_UPDATE:
  277.                         Update(City, Token);
  278.                         RedrawGUI();
  279.                         break;
  280.                     case BTN_QUIT:
  281.                         exit(0);
  282.                         break;
  283.                 }
  284.         }
  285.     }
  286.     return 0;
  287. }
  288.