├── example ├── screenshot.png ├── example_top.jpeg ├── example_bottom.jpeg └── example.pcb ├── README.md ├── Makefile ├── pcb.h ├── display.c ├── main.c └── event.c /example/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unixdj/depcb/HEAD/example/screenshot.png -------------------------------------------------------------------------------- /example/example_top.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unixdj/depcb/HEAD/example/example_top.jpeg -------------------------------------------------------------------------------- /example/example_bottom.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unixdj/depcb/HEAD/example/example_bottom.jpeg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Requires ``gtk+-2.0`` and ``goocanvas`` 2 | 3 | apt-get install libgtk2.0-dev libgoocanvas-dev 4 | 5 | Install: 6 | 7 | git clone https://github.com/unixdj/depcb.git 8 | cd depcb 9 | make 10 | 11 | Run: 12 | 13 | ./depcb save.pcb layer1.jpg layer2.jpg ... 14 | 15 | Example: 16 | 17 | cd example; ../depcb example.pcb 18 | 19 | Pressing ``h`` in window will print a list of shortcut keys. 20 | 21 | ![screenshot](./example/screenshot.png) 22 | 23 | by http://www.vygo.net/vadik/ 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DEBUG = -g 2 | #DEBUG = -O2 3 | GTK_CFLAGS := $(shell pkg-config --cflags gtk+-2.0 goocanvas) 4 | GTK_LDFLAGS := $(shell pkg-config --libs gtk+-2.0 goocanvas) 5 | CFLAGS += $(DEBUG) -Wall -Wextra -Werror $(GTK_CFLAGS) 6 | LDFLAGS += $(DEBUG) $(GTK_LDFLAGS) 7 | SRCS = main.c event.c display.c 8 | OBJS = $(SRCS:.c=.o) 9 | HDRS = pcb.h 10 | PROG = depcb 11 | 12 | all: $(PROG) 13 | 14 | $(OBJS): $(HDRS) 15 | 16 | $(PROG): $(OBJS) 17 | $(CC) $(OBJS) $(LDFLAGS) -o $(PROG) 18 | 19 | clean: 20 | -rm -rf $(PROG) $(OBJS) 21 | 22 | scope: 23 | cscope -bq $(SRCS) $(HDRS) 24 | -------------------------------------------------------------------------------- /example/example.pcb: -------------------------------------------------------------------------------- 1 | depcb-project-0 2 | layers 2 3 | curlayer 0 4 | size 1600.0 1200.0 5 | imagefile example_bottom.jpeg 6 | imagefile example_top.jpeg 7 | point 3 904.5 394.5 0 8 | point 3 896.5 403.5 0 9 | point 3 905.2 413.2 0 10 | point 3 951.2 458.8 0 11 | point 3 991.2 423.2 0 12 | point 3 1059.8 435.2 0 13 | point 3 1060.2 400.2 0 14 | point 3 1058.8 364.2 0 15 | point 3 899.2 438.8 0 16 | point 3 993.2 487.8 0 17 | point 3 994.2 506.2 0 18 | point 3 972.8 501.2 0 19 | point 3 1039.2 516.8 0 20 | point 3 1069.2 514.8 0 21 | point 3 1045.2 488.8 0 22 | point 3 1045.2 470.2 0 23 | point 3 1052.8 426.8 0 24 | line 2 1059.8 435.2 1052.8 426.8 25 | line 2 1052.8 426.8 991.2 423.2 26 | line 2 991.2 423.2 993.2 487.8 27 | point 3 993.2 457.2 0 28 | line 1 993.2 487.8 993.2 457.2 29 | point 3 1013.2 456.2 0 30 | line 1 993.2 457.2 1013.2 456.2 31 | point 3 1011.8 405.2 0 32 | line 1 1013.2 456.2 1011.8 405.2 33 | point 3 927.2 404.8 0 34 | line 1 1011.8 405.2 927.2 404.8 35 | point 3 922.8 416.2 0 36 | line 1 927.2 404.8 922.8 416.2 37 | line 1 922.8 416.2 905.2 413.2 38 | point 3 902.8 423.8 0 39 | line 1 905.2 413.2 902.8 423.8 40 | point 3 881.2 422.8 0 41 | line 1 902.8 423.8 881.2 422.8 42 | point 3 878.8 455.2 0 43 | line 1 881.2 422.8 878.8 455.2 44 | point 3 894.8 456.2 0 45 | line 1 878.8 455.2 894.8 456.2 46 | point 3 893.8 505.8 0 47 | line 1 894.8 456.2 893.8 505.8 48 | point 3 896.2 654.2 0 49 | line 1 893.8 505.8 896.2 654.2 50 | point 3 878.8 506.2 0 51 | line 1 893.8 505.8 878.8 506.2 52 | point 3 917.8 507.8 0 53 | line 2 878.8 506.2 917.8 507.8 54 | point 3 917.2 556.2 0 55 | line 2 917.8 507.8 917.2 556.2 56 | point 3 854.2 561.2 0 57 | line 2 917.2 556.2 854.2 561.2 58 | point 3 847.8 549.2 0 59 | line 2 854.2 561.2 847.8 549.2 60 | point 3 848.2 488.2 0 61 | point 3 836.8 488.8 0 62 | line 1 848.2 488.2 836.8 488.8 63 | point 3 831.2 504.8 0 64 | line 1 836.8 488.8 831.2 504.8 65 | point 3 859.2 487.2 0 66 | line 1 848.2 488.2 859.2 487.2 67 | point 3 861.8 455.2 0 68 | line 1 859.2 487.2 861.8 455.2 69 | point 3 878.8 488.8 0 70 | line 1 859.2 487.2 878.8 488.8 71 | point 3 861.2 422.2 0 72 | line 1 861.8 455.2 861.2 422.2 73 | point 3 823.2 419.8 0 74 | line 1 861.2 422.2 823.2 419.8 75 | point 3 810.2 431.2 0 76 | line 1 823.2 419.8 810.2 431.2 77 | point 3 787.8 430.2 0 78 | line 1 810.2 431.2 787.8 430.2 79 | point 3 787.8 367.2 0 80 | line 1 787.8 430.2 787.8 367.2 81 | line 1 787.8 367.2 1058.8 364.2 82 | -------------------------------------------------------------------------------- /pcb.h: -------------------------------------------------------------------------------- 1 | #ifndef __PCB_H 2 | #define __PCB_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Tokens per line of file */ 9 | #define PCB_MAX_TOKENS 8 10 | 11 | typedef struct { 12 | gdouble x, y; 13 | } PcbCoordinate; 14 | 15 | /* type */ 16 | #define PCB_EMPTY 0 17 | #define PCB_POINT 1 18 | #define PCB_LINE 2 19 | 20 | /* flags - saved */ 21 | #define PCB_POI 0x0001 22 | #define PCB_BEND 0x0002 23 | #define PCB_SAVED_FLAGS 0x000f 24 | /* flags - stored in undo list */ 25 | #define PCB_SELECTED 0x0010 26 | #define PCB_STORED_FLAGS 0x00ff 27 | /* flags - private */ 28 | #define PCB_MARKED 0x0100 29 | 30 | typedef struct _PcbItem { 31 | struct _PcbItem *next; 32 | unsigned long long layers; 33 | int type; 34 | int flags; 35 | union { 36 | PcbCoordinate p; 37 | struct { 38 | struct _PcbItem *point[2]; 39 | } l; 40 | }; 41 | GooCanvasItem *canvas_item[0]; 42 | } PcbItem; 43 | 44 | #define new_pcb_item(pcb) \ 45 | g_malloc0(sizeof(PcbItem) + (pcb).layers * sizeof(GooCanvasItem *)) 46 | 47 | #define LAYER(layer) ((unsigned long long)1 << (layer)) 48 | #define CUR_LAYER() LAYER(pcb.curlayer) 49 | #define LAYERS_BELOW(bit) ((bit) - 1) 50 | #define ALL_LAYERS() LAYERS_BELOW(LAYER(pcb.layers)) 51 | #define LAYERS_BETWEEN(a, b) (LAYERS_BELOW(MAX(a, b) << 1) ^ \ 52 | LAYERS_BELOW(MIN(a, b))) 53 | 54 | /* action */ 55 | #define PCB_ITEM 0x0f 56 | #define PCB_ACTION 0xf0 57 | 58 | /* "undo" flag */ 59 | #define PCB_DO 0x00 60 | #define PCB_UNDO 0x10 61 | /* actions */ 62 | #define PCB_ADD 0x20 63 | #define PCB_REMOVE 0x30 64 | #define PCB_TOGGLE_FLAGS 0x40 65 | #define PCB_UNTOGGLE_FLAGS 0x50 66 | #define PCB_CHANGE_LAYERS 0x60 67 | #define PCB_UNCHANGE_LAYERS 0x70 68 | /* last action in a transaction */ 69 | #define PCB_MARKER 0x100 70 | 71 | typedef struct _PcbAction { 72 | struct _PcbAction *next, *prev; 73 | int act; 74 | int flags; 75 | unsigned long long layers, newlayers; 76 | PcbCoordinate c; 77 | union { 78 | PcbCoordinate l; 79 | }; 80 | } PcbAction; 81 | 82 | typedef struct { 83 | GdkPixbuf *img; 84 | GooCanvas *canvas; 85 | GooCanvasItem *root, *olay; 86 | char *filename; 87 | } PcbLayer; 88 | 89 | /* mode */ 90 | #define PCB_SELECT 0 91 | #define PCB_ADD_POINT 1 92 | #define PCB_ADD_VIA 2 93 | #define PCB_ADD_LINE 3 94 | #define PCB_EXAMINE 4 95 | #define PCB_TRACE 5 96 | 97 | /* flags */ 98 | #define PCB_DIRTY 0x1 99 | #define PCB_AUTOLIMIT 0x2 100 | 101 | typedef struct { 102 | GtkWidget *window, *scrolled; 103 | PcbLayer *layer; 104 | PcbItem *items; 105 | PcbAction *action, *new_action; 106 | gdouble width, height, scale; 107 | int layers, curlayer, marked_layer; 108 | int mode; 109 | int flags; 110 | int selected; 111 | PcbCoordinate c[2]; 112 | int coords; 113 | char *filename; 114 | } Pcb; 115 | 116 | extern Pcb pcb; 117 | 118 | PcbItem * find_point(PcbCoordinate c, unsigned long long layer); 119 | int play_action(int flag); 120 | void save_project(int); 121 | 122 | void redisplay(void); 123 | void undisplay(void); 124 | void show_item(PcbItem *item); 125 | void hide_item(PcbItem *item); 126 | void show_overlays(int layer); 127 | void hide_overlays(int layer); 128 | void init_layer_events(GooCanvasItem *w); 129 | void init_window_events(GtkWidget *w); 130 | void init_display(void); 131 | 132 | /* Coordinate equality (for the purpose of matching/clashing points */ 133 | #define ceq(a, b) \ 134 | (fabs((a).x - (b).x) < 1.5 && (fabs((a).y - (b).y)) < 1.5) 135 | 136 | #endif /* __PCB_H */ 137 | -------------------------------------------------------------------------------- /display.c: -------------------------------------------------------------------------------- 1 | #include "pcb.h" 2 | 3 | static void 4 | hide_item_layer(PcbItem *item, int layer) 5 | { 6 | if (item->canvas_item[layer]) { 7 | goo_canvas_item_remove(item->canvas_item[layer]); 8 | item->canvas_item[layer] = NULL; 9 | } 10 | } 11 | 12 | void 13 | hide_item(PcbItem *item) 14 | { 15 | int i; 16 | 17 | for (i = 0; i < pcb.layers; i++) 18 | hide_item_layer(item, i); 19 | } 20 | 21 | static void 22 | show_point(PcbItem *item, int layer) 23 | { 24 | #define rad 3.0 25 | #define siz 3.5 26 | if (item->flags & PCB_POI) 27 | item->canvas_item[layer] = 28 | goo_canvas_rect_new(pcb.layer[layer].olay, 29 | item->p.x - siz, item->p.y - siz, siz * 2, siz * 2, 30 | "stroke-color", "black", "fill-color", 31 | item->flags & PCB_SELECTED ? "red" : "yellow", NULL); 32 | else if (item->flags & PCB_BEND) 33 | item->canvas_item[layer] = 34 | goo_canvas_ellipse_new(pcb.layer[layer].olay, 35 | item->p.x, item->p.y, 2, 2, 36 | "stroke-color", "black", "fill-color", 37 | item->flags & PCB_SELECTED ? "green" : "black", NULL); 38 | else 39 | item->canvas_item[layer] = 40 | goo_canvas_ellipse_new(pcb.layer[layer].olay, 41 | item->p.x, item->p.y, rad, rad, 42 | "stroke-color", "black", "fill-color", 43 | item->flags & PCB_SELECTED ? "green" : "white", NULL); 44 | #undef rad 45 | #undef siz 46 | } 47 | 48 | static void 49 | show_line(PcbItem *item, int layer) 50 | { 51 | item->canvas_item[layer] = goo_canvas_polyline_new_line(pcb.layer[layer].olay, 52 | item->l.point[0]->p.x, item->l.point[0]->p.y, 53 | item->l.point[1]->p.x, item->l.point[1]->p.y, 54 | "stroke-color", item->flags & PCB_SELECTED ? "green" : "black", 55 | NULL); 56 | } 57 | 58 | static void 59 | show_item_layer(PcbItem *item, int layer) 60 | { 61 | if (!pcb.layer->olay) 62 | return; 63 | hide_item_layer(item, layer); 64 | if (!(item->layers & LAYER(layer))) 65 | return; 66 | switch (item->type) { 67 | case PCB_POINT: 68 | show_point(item, layer); 69 | break; 70 | case PCB_LINE: 71 | show_line(item, layer); 72 | break; 73 | } 74 | } 75 | 76 | void 77 | show_item(PcbItem *item) 78 | { 79 | int i; 80 | 81 | for (i = 0; i < pcb.layers; i++) 82 | show_item_layer(item, i); 83 | } 84 | 85 | void 86 | undisplay(void) 87 | { 88 | gtk_container_remove(GTK_CONTAINER(pcb.scrolled), 89 | GTK_WIDGET(pcb.layer[pcb.curlayer].canvas)); 90 | } 91 | 92 | void 93 | redisplay(void) 94 | { 95 | gtk_container_add(GTK_CONTAINER(pcb.scrolled), 96 | GTK_WIDGET(pcb.layer[pcb.curlayer].canvas)); 97 | gtk_widget_show_all(pcb.window); 98 | } 99 | 100 | void 101 | show_overlays(int layer) 102 | { 103 | PcbItem *cur; 104 | 105 | g_print("show overlays on layer %d\n", layer); 106 | pcb.layer[layer].olay = goo_canvas_group_new(pcb.layer[layer].root, 107 | "x", 0.0, "y", 0.0, "width", pcb.width, "height", pcb.height, NULL); 108 | for (cur = pcb.items; cur; cur = cur->next) 109 | show_item_layer(cur, layer); 110 | } 111 | 112 | void 113 | hide_overlays(int layer) 114 | { 115 | PcbItem *cur; 116 | 117 | g_print("hide overlays on layer %d\n", layer); 118 | if (pcb.layer[layer].olay) { 119 | goo_canvas_item_remove(pcb.layer[layer].olay); 120 | pcb.layer[layer].olay = NULL; 121 | } 122 | for (cur = pcb.items; cur; cur = cur->next) 123 | cur->canvas_item[layer] = NULL; 124 | } 125 | 126 | void 127 | init_display() 128 | { 129 | int i; 130 | 131 | pcb.scale = 1.0; 132 | for (i = 0; i < pcb.layers; i++) { 133 | pcb.layer[i].canvas = GOO_CANVAS(goo_canvas_new()); 134 | gtk_object_ref(GTK_OBJECT(pcb.layer[i].canvas)); 135 | goo_canvas_set_bounds(pcb.layer[i].canvas, 136 | 0, 0, pcb.width, pcb.height); 137 | pcb.layer[i].root = 138 | goo_canvas_get_root_item(pcb.layer[i].canvas); 139 | goo_canvas_image_new(pcb.layer[i].root, pcb.layer[i].img, 0, 0, 140 | NULL); 141 | init_layer_events(pcb.layer[i].root); 142 | show_overlays(i); 143 | } 144 | 145 | pcb.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 146 | gtk_window_set_default_size(GTK_WINDOW(pcb.window), 800, 600); 147 | gtk_window_set_title(GTK_WINDOW(pcb.window), pcb.filename); 148 | 149 | init_window_events(pcb.window); 150 | 151 | pcb.scrolled = gtk_scrolled_window_new(NULL, NULL); 152 | gtk_widget_show(pcb.scrolled); 153 | gtk_container_add(GTK_CONTAINER(pcb.window), pcb.scrolled); 154 | 155 | redisplay(); 156 | } 157 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "pcb.h" 8 | 9 | Pcb pcb; /* One for now */ 10 | 11 | static PcbItem * 12 | find_action_item(PcbItem ***cur, int unchange) 13 | { 14 | PcbItem **stor; 15 | 16 | if (!cur) 17 | cur = &stor; 18 | for (*cur = &pcb.items; **cur; *cur = &(**cur)->next) { 19 | if ((**cur)->type != (pcb.action->act & PCB_ITEM) || 20 | (**cur)->layers != 21 | (unchange ? pcb.action->newlayers : pcb.action->layers)) 22 | continue; 23 | switch ((**cur)->type) { 24 | case PCB_POINT: 25 | if (ceq((**cur)->p, pcb.action->c)) 26 | return **cur; 27 | break; 28 | case PCB_LINE: 29 | if ((ceq((**cur)->l.point[0]->p, pcb.action->c) && 30 | ceq((**cur)->l.point[1]->p, pcb.action->l)) || 31 | (ceq((**cur)->l.point[0]->p, pcb.action->l) && 32 | ceq((**cur)->l.point[1]->p, pcb.action->c))) 33 | return **cur; 34 | break; 35 | } 36 | } 37 | return **cur; 38 | } 39 | 40 | static int 41 | play_add(void) 42 | { 43 | PcbItem **cur; 44 | 45 | if (find_action_item(&cur, 0)) { 46 | switch (pcb.action->act & PCB_ITEM) { 47 | case PCB_POINT: 48 | g_warning("invalid add point: %f %f\n", 49 | pcb.action->c.x, pcb.action->c.y); 50 | break; 51 | case PCB_LINE: 52 | g_warning("invalid add line: %f %f %f %f\n", 53 | pcb.action->c.x, pcb.action->c.y, 54 | pcb.action->l.x, pcb.action->l.y); 55 | break; 56 | } 57 | return 0; 58 | } 59 | *cur = new_pcb_item(pcb); 60 | (*cur)->type = pcb.action->act & PCB_ITEM; 61 | (*cur)->layers = pcb.action->layers; 62 | (*cur)->flags = pcb.action->flags; 63 | switch ((*cur)->type) { 64 | case PCB_POINT: 65 | (*cur)->p = pcb.action->c; 66 | g_print("add point [%llx] (%f %f)\n", (*cur)->layers, 67 | (*cur)->p.x, (*cur)->p.y); 68 | break; 69 | case PCB_LINE: 70 | if (((*cur)->l.point[0] = 71 | find_point(pcb.action->c, pcb.action->layers)) && 72 | ((*cur)->l.point[1] = 73 | find_point(pcb.action->l, pcb.action->layers))) { 74 | g_print("add line [%llx] (%f %f) (%f %f)\n", 75 | (*cur)->layers, 76 | (*cur)->l.point[0]->p.x, (*cur)->l.point[0]->p.y, 77 | (*cur)->l.point[1]->p.x, (*cur)->l.point[1]->p.y); 78 | break; 79 | } 80 | g_warning("invalid line: %f %f %f %f\n", pcb.action->c.x, 81 | pcb.action->c.y, pcb.action->l.x, pcb.action->l.y); 82 | /* FAlLTHRU */ 83 | default: 84 | g_free(*cur); 85 | *cur = NULL; 86 | return 0; 87 | } 88 | show_item(*cur); 89 | return 1; 90 | } 91 | 92 | static int 93 | play_remove(void) 94 | { 95 | PcbItem **cur; 96 | PcbItem *this; 97 | 98 | if (!find_action_item(&cur, 0)) { 99 | switch (pcb.action->act & PCB_ITEM) { 100 | case PCB_POINT: 101 | g_warning("invalid remove point: %f %f\n", 102 | pcb.action->c.x, pcb.action->c.y); 103 | break; 104 | case PCB_LINE: 105 | g_warning("invalid remove line: %f %f %f %f\n", 106 | pcb.action->c.x, pcb.action->c.y, 107 | pcb.action->l.x, pcb.action->l.y); 108 | break; 109 | } 110 | return 0; 111 | } 112 | 113 | switch ((*cur)->type) { 114 | case PCB_POINT: 115 | g_print("remove point [%llx] (%f %f)\n", (*cur)->layers, 116 | (*cur)->p.x, (*cur)->p.y); 117 | break; 118 | case PCB_LINE: 119 | g_print("remove line [%llx] (%f %f) (%f %f)\n", (*cur)->layers, 120 | (*cur)->l.point[0]->p.x, (*cur)->l.point[0]->p.y, 121 | (*cur)->l.point[1]->p.x, (*cur)->l.point[1]->p.y); 122 | break; 123 | } 124 | this = *cur; 125 | (*cur) = this->next; 126 | hide_item(this); 127 | pcb.action->layers = this->layers; 128 | pcb.action->flags = this->flags & PCB_STORED_FLAGS; 129 | g_free(this); 130 | return 1; 131 | } 132 | 133 | static int 134 | play_toggle_flags() 135 | { 136 | PcbItem **cur; 137 | 138 | if ((pcb.action->act & PCB_ITEM) != PCB_POINT) { 139 | g_warning("invalid toggle: wrong item type\n"); 140 | return 0; 141 | } 142 | if (!find_action_item(&cur, 0)) { 143 | g_warning("invalid toggle: %f %f\n", 144 | pcb.action->c.x, pcb.action->c.y); 145 | return 0; 146 | } 147 | (*cur)->flags ^= pcb.action->flags; 148 | g_print("toggle flags %x [%llx] (%f %f)\n", pcb.action->flags, 149 | (*cur)->layers, (*cur)->p.x, (*cur)->p.y); 150 | show_item(*cur); 151 | return 1; 152 | } 153 | 154 | static int 155 | play_change_layers(int unchange) 156 | { 157 | PcbItem **cur; 158 | 159 | if ((pcb.action->act & PCB_ITEM) != PCB_POINT) { 160 | g_warning("invalid change layers: wrong item type\n"); 161 | return 0; 162 | } 163 | if (!find_action_item(&cur, unchange)) { 164 | g_warning("invalid toggle layers: %f %f\n", 165 | pcb.action->c.x, pcb.action->c.y); 166 | return 0; 167 | } 168 | (*cur)->layers = unchange ? pcb.action->layers : pcb.action->newlayers; 169 | g_print("change layers to [%llx] (%f %f)\n", (*cur)->layers, 170 | (*cur)->p.x, (*cur)->p.y); 171 | show_item(*cur); 172 | return 1; 173 | } 174 | 175 | int 176 | play_action(int flag) 177 | { 178 | switch ((pcb.action->act & PCB_ACTION) ^ flag) { 179 | case PCB_ADD: 180 | return play_add(); 181 | case PCB_REMOVE: 182 | return play_remove(); 183 | case PCB_TOGGLE_FLAGS: 184 | case PCB_UNTOGGLE_FLAGS: 185 | return play_toggle_flags(); 186 | case PCB_CHANGE_LAYERS: 187 | case PCB_UNCHANGE_LAYERS: 188 | return play_change_layers(flag); 189 | } 190 | return 0; 191 | } 192 | 193 | void 194 | save_project(int dummy) 195 | { 196 | FILE *f; 197 | PcbItem *item; 198 | int i; 199 | 200 | (void)dummy; 201 | if (!(f = fopen(pcb.filename, "w"))) { 202 | g_warning("Can't open file: %s: %s\n", pcb.filename, 203 | strerror(errno)); 204 | return; 205 | } 206 | fprintf(f, "depcb-project-0\nlayers %d\ncurlayer %d\nsize %.1f %.1f\n", 207 | pcb.layers, pcb.curlayer, pcb.width, pcb.height); 208 | for (i = 0; i < pcb.layers; i++) 209 | fprintf(f, "imagefile %s\n", pcb.layer[i].filename); 210 | for (item = pcb.items; item; item = item->next) { 211 | switch (item->type) { 212 | case PCB_POINT: 213 | fprintf(f, "point %llx %.1f %.1f %d\n", 214 | item->layers, item->p.x, item->p.y, 215 | item->flags & PCB_SAVED_FLAGS); 216 | break; 217 | case PCB_LINE: 218 | fprintf(f, "line %llx %.1f %.1f %.1f %.1f\n", 219 | item->layers, 220 | item->l.point[0]->p.x, item->l.point[0]->p.y, 221 | item->l.point[1]->p.x, item->l.point[1]->p.y); 222 | break; 223 | } 224 | } 225 | fclose(f); 226 | pcb.flags &= ~PCB_DIRTY; 227 | } 228 | 229 | static void 230 | tokenize(char *s, char **tok, int *tokens) 231 | { 232 | *tokens = 0; 233 | while (*tokens < PCB_MAX_TOKENS) { 234 | while (*s && isspace(*s)) 235 | s++; 236 | if (!*s || *tokens == PCB_MAX_TOKENS) 237 | break; 238 | tok[(*tokens)++] = s; 239 | while (*s && !isspace(*s)) 240 | s++; 241 | if (!*s) 242 | break; 243 | *s++ = 0; 244 | } 245 | } 246 | 247 | static int 248 | load_project(char *filename) 249 | { 250 | char buf[PATH_MAX]; 251 | char *tok[PCB_MAX_TOKENS]; 252 | FILE *f; 253 | char *s; 254 | int tokens = 0; 255 | int state = 0, line = 1, layer = 0; 256 | 257 | #define EXPECT(t, s) \ 258 | if (t != tokens) { \ 259 | g_error("%s line %d: Expected %d tokens, got %d\n", \ 260 | filename, line, t, tokens); \ 261 | goto Error; \ 262 | } \ 263 | if (strcmp(tok[0], s)) { \ 264 | g_error("%s line %d: Expected %s, got %s\n", \ 265 | filename, line, s, tok[0]); \ 266 | goto Error; \ 267 | } 268 | #define PARSE_ERROR() \ 269 | do { \ 270 | g_error("%s line %d: Parse error\n", filename, line); \ 271 | goto Error; \ 272 | } while (0) 273 | #define GET_LAYER(l, s) \ 274 | l = strtoull(s, NULL, 16); \ 275 | if (!l || (l & ~ALL_LAYERS())) \ 276 | PARSE_ERROR(); 277 | #define GET_COORD(c, x0, y0) \ 278 | (c).x = atof(x0); \ 279 | (c).y = atof(y0); \ 280 | if ((c).x < 0 || (c).x > pcb.width || \ 281 | (c).y < 0 || (c).y > pcb.height) \ 282 | PARSE_ERROR(); 283 | 284 | pcb.filename = filename; 285 | if (!(f = fopen(pcb.filename, "r"))) { 286 | g_error("Can't open file: %s: %s\n", pcb.filename, 287 | strerror(errno)); 288 | return -1; 289 | } 290 | while (fgets(buf, sizeof(buf), f)) { 291 | if (state != 0 && state != 4) 292 | tokenize(buf, tok, &tokens); 293 | switch (state) { 294 | case 0: 295 | if (strcmp(buf, "depcb-project-0\n")) { 296 | g_error("Not a project file: %s\n%s%s", 297 | pcb.filename, "depcb-project-0\n", buf); 298 | goto Error; 299 | } 300 | state++; 301 | break; 302 | case 1: 303 | EXPECT(2, "layers"); 304 | pcb.layers = atoi(tok[1]); 305 | if (pcb.layers < 1 || pcb.layers > 64) 306 | PARSE_ERROR(); 307 | pcb.layer = g_malloc0(pcb.layers * sizeof(*pcb.layer)); 308 | pcb.action = g_malloc0(sizeof(PcbAction)); 309 | state++; 310 | break; 311 | case 2: 312 | EXPECT(2, "curlayer"); 313 | pcb.curlayer = atoi(tok[1]); 314 | if (pcb.curlayer < 0 || pcb.curlayer >= pcb.layers) 315 | PARSE_ERROR(); 316 | state++; 317 | break; 318 | case 3: 319 | EXPECT(3, "size"); 320 | pcb.width = atof(tok[1]); 321 | pcb.height = atof(tok[2]); 322 | state++; 323 | break; 324 | case 4: 325 | if (strncmp(buf, "imagefile ", 10)) { 326 | g_error("expected imagefile, got %s", buf); 327 | goto Error; 328 | } 329 | s = buf + strlen(buf) - 1; 330 | if (*s == '\n') 331 | *s = 0; 332 | pcb.layer[layer].filename = strdup(buf + 10); 333 | if (++layer == pcb.layers) 334 | state++; 335 | break; 336 | case 5: 337 | if (tokens == 5 && !strcmp(tok[0], "point")) { 338 | pcb.action->act = PCB_ADD | PCB_POINT; 339 | GET_LAYER(pcb.action->layers, tok[1]); 340 | GET_COORD(pcb.action->c, tok[2], tok[3]); 341 | pcb.action->flags = strtol(tok[4], NULL, 16); 342 | if (!play_action(PCB_DO)) 343 | PARSE_ERROR(); 344 | break; 345 | } 346 | if (tokens == 6 && !strcmp(tok[0], "line")) { 347 | pcb.action->act = PCB_ADD | PCB_LINE; 348 | GET_LAYER(pcb.action->layers, tok[1]); 349 | GET_COORD(pcb.action->c, tok[2], tok[3]); 350 | GET_COORD(pcb.action->l, tok[4], tok[5]); 351 | if (!play_action(PCB_DO)) 352 | PARSE_ERROR(); 353 | break; 354 | } 355 | bzero(pcb.action, sizeof(*pcb.action)); 356 | state++; 357 | /* FALLTHRU */ 358 | case 6: 359 | g_error("garbage at eof"); 360 | break; 361 | } 362 | line++; 363 | } 364 | return 0; 365 | Error: 366 | fclose(f); 367 | return -1; 368 | } 369 | 370 | static int 371 | new_project(int argc, char *argv[]) 372 | { 373 | int i; 374 | 375 | pcb.layers = argc - 2; 376 | pcb.layer = g_malloc0(pcb.layers * sizeof(*pcb.layer)); 377 | pcb.action = g_malloc0(sizeof(PcbAction)); 378 | pcb.filename = argv[1]; 379 | for (i = 0; i < pcb.layers; i++) 380 | pcb.layer[i].filename = argv[i + 2]; 381 | return 0; 382 | } 383 | 384 | static void 385 | init_layers(int new) 386 | { 387 | GError *error = NULL; 388 | int i; 389 | 390 | pcb.marked_layer = -1; 391 | for (i = 0; i < pcb.layers; i++) { 392 | g_print("loading %s\n", pcb.layer[i].filename); 393 | if (!(pcb.layer[i].img = 394 | gdk_pixbuf_new_from_file(pcb.layer[i].filename, &error))) { 395 | errx(1, "Can't open file %s: %s", 396 | pcb.layer[i].filename, error->message); 397 | } 398 | if (new) { 399 | int cur; 400 | 401 | cur = gdk_pixbuf_get_width(pcb.layer[i].img); 402 | pcb.width = MAX(pcb.width, cur); 403 | cur = gdk_pixbuf_get_height(pcb.layer[i].img); 404 | pcb.height = MAX(pcb.height, cur); 405 | } 406 | } 407 | } 408 | 409 | #define PROGNAME "DePCB (working title) 0.0pl0" 410 | static void 411 | pcb_init(int argc, char *argv[]) 412 | { 413 | int new = 0; 414 | 415 | switch (argc) { 416 | case 1: 417 | errx(1, PROGNAME "\n\nUsage:\n" 418 | "\t%s .pcb - load project\n" 419 | "\t%s .pcb ... - start a new project", 420 | argv[0], argv[0]); 421 | /* NOTREACHED */ 422 | case 2: 423 | load_project(argv[1]); 424 | break; 425 | default: 426 | if (argc > 66) 427 | errx(1, "More than 64 layers? " 428 | "You've got to be kidding me\n"); 429 | new = 1; 430 | new_project(argc, argv); 431 | break; 432 | } 433 | g_print("springloaded\n"); 434 | init_layers(new); 435 | init_display(); 436 | } 437 | 438 | int 439 | main(int argc, char *argv[]) 440 | { 441 | gtk_init(&argc, &argv); 442 | pcb_init(argc, argv); 443 | g_print("\n\n" PROGNAME "\nPress \"h\" in main window for help\n\n\n"); 444 | gtk_main(); 445 | g_print("exit 0\n"); 446 | return 0; 447 | } 448 | -------------------------------------------------------------------------------- /event.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "pcb.h" 4 | 5 | static void 6 | undo(int dummy) 7 | { 8 | (void)dummy; 9 | while (pcb.action->prev) { 10 | play_action(PCB_UNDO); 11 | pcb.action = pcb.action->prev; 12 | if (pcb.action->act & PCB_MARKER) 13 | break; 14 | } 15 | pcb.flags |= PCB_DIRTY; 16 | } 17 | 18 | static void 19 | redo(int dummy) 20 | { 21 | (void)dummy; 22 | if (pcb.action->next) do { 23 | pcb.action = pcb.action->next; 24 | play_action(PCB_DO); 25 | } while (pcb.action->next && !(pcb.action->act & PCB_MARKER)); 26 | pcb.flags |= PCB_DIRTY; 27 | } 28 | 29 | #define init_transaction() do { pcb.new_action = pcb.action; } while (0) 30 | 31 | static void 32 | commit_transaction(void) 33 | { 34 | if (pcb.action != pcb.new_action) { 35 | pcb.new_action->act |= PCB_MARKER; 36 | redo(0); 37 | } 38 | } 39 | 40 | static void 41 | new_action(void) 42 | { 43 | PcbAction *cur; 44 | 45 | while (pcb.new_action->next) { 46 | cur = pcb.new_action->next; 47 | pcb.new_action->next = cur->next; 48 | g_free(cur); 49 | } 50 | pcb.new_action->next = g_malloc0(sizeof(PcbAction)); 51 | pcb.new_action->next->prev = pcb.new_action; 52 | pcb.new_action = pcb.new_action->next; 53 | } 54 | 55 | static void 56 | new_item_action(int act, PcbItem *item) 57 | { 58 | new_action(); 59 | pcb.new_action->act = act | item->type; 60 | pcb.new_action->layers = item->layers; 61 | switch (item->type) { 62 | case PCB_POINT: 63 | pcb.new_action->c = item->p; 64 | break; 65 | case PCB_LINE: 66 | pcb.new_action->c = item->l.point[0]->p; 67 | pcb.new_action->l = item->l.point[1]->p; 68 | break; 69 | } 70 | } 71 | 72 | #define remove_item(item) new_item_action(PCB_REMOVE, item) 73 | 74 | #define change_layers(item, layers) do { \ 75 | new_item_action(PCB_CHANGE_LAYERS, item); \ 76 | pcb.new_action->newlayers = layers; \ 77 | } while (0) 78 | 79 | /* layers is 0 for layers where lines should be removed */ 80 | static void 81 | remove_connected_lines(PcbItem *point, unsigned long long layers) 82 | { 83 | PcbItem *cur; 84 | 85 | for (cur = point->next; cur; cur = cur->next) { 86 | if (cur->type == PCB_LINE && !(cur->layers & layers) && 87 | !(cur->flags & PCB_MARKED) && 88 | (cur->l.point[0] == point || cur->l.point[1] == point)) { 89 | cur->flags |= PCB_MARKED; 90 | remove_item(cur); 91 | } 92 | } 93 | } 94 | 95 | static void 96 | modify_layers_selected(unsigned long long layers) 97 | { 98 | PcbItem *cur; 99 | 100 | init_transaction(); 101 | for (cur = pcb.items; cur; cur = cur->next) { 102 | if (cur->flags & PCB_SELECTED) { 103 | if (cur->type == PCB_POINT) 104 | remove_connected_lines(cur, layers); 105 | if (layers) 106 | change_layers(cur, layers); 107 | else 108 | remove_item(cur); 109 | } 110 | } 111 | commit_transaction(); 112 | } 113 | 114 | static void 115 | change_layers_selected(int dummy) 116 | { 117 | unsigned long long layers; 118 | 119 | (void)dummy; 120 | if (pcb.mode != PCB_SELECT) 121 | return; 122 | if (pcb.marked_layer == -1) { 123 | pcb.marked_layer = pcb.curlayer; 124 | return; 125 | } 126 | layers = LAYERS_BETWEEN(LAYER(pcb.marked_layer), CUR_LAYER()); 127 | g_print("change layers to [%llx]\n", layers); 128 | pcb.marked_layer = -1; 129 | modify_layers_selected(layers); 130 | } 131 | 132 | static void 133 | delete_selected(int dummy) 134 | { 135 | (void)dummy; 136 | if (pcb.mode == PCB_SELECT) 137 | modify_layers_selected(0); 138 | } 139 | 140 | static void 141 | toggle_poi_selected(int dummy) 142 | { 143 | PcbItem *cur; 144 | 145 | (void)dummy; 146 | init_transaction(); 147 | if (pcb.mode != PCB_SELECT) 148 | return; 149 | for (cur = pcb.items; cur; cur = cur->next) { 150 | if ((cur->flags & PCB_SELECTED) && cur->type == PCB_POINT) { 151 | new_item_action(PCB_TOGGLE_FLAGS, cur); 152 | pcb.new_action->flags = PCB_POI; 153 | } 154 | } 155 | commit_transaction(); 156 | } 157 | 158 | PcbItem * 159 | find_point(PcbCoordinate c, unsigned long long layers) 160 | { 161 | PcbItem *cur; 162 | 163 | for (cur = pcb.items; cur; cur = cur->next) { 164 | if (cur->type == PCB_POINT && (cur->layers & layers) && 165 | ceq(cur->p, c)) 166 | break; 167 | } 168 | return cur; 169 | } 170 | 171 | static void 172 | select_item(PcbItem *item) 173 | { 174 | if (!item) 175 | return; 176 | if ((item->flags ^= PCB_SELECTED) & PCB_SELECTED) 177 | pcb.selected++; 178 | else 179 | pcb.selected--; 180 | show_item(item); 181 | } 182 | 183 | static void 184 | deselect_all(int dummy) 185 | { 186 | PcbItem *cur; 187 | 188 | (void)dummy; 189 | if (pcb.selected) { 190 | for (cur = pcb.items; cur; cur = cur->next) { 191 | if (cur->flags & PCB_SELECTED) 192 | select_item(cur); 193 | } 194 | pcb.selected = 0; 195 | } 196 | pcb.marked_layer = -1; 197 | pcb.coords = 0; 198 | } 199 | 200 | static void 201 | add_point(PcbCoordinate c, unsigned long long layers) 202 | { 203 | PcbItem *item; 204 | 205 | if ((item = find_point(c, layers))) { 206 | g_print("conflicting point found, not adding\n"); 207 | deselect_all(0); 208 | select_item(item); 209 | return; 210 | } 211 | init_transaction(); 212 | new_action(); 213 | pcb.new_action->act = PCB_ADD | PCB_POINT; 214 | pcb.new_action->layers = layers; 215 | pcb.new_action->c = c; 216 | commit_transaction(); 217 | } 218 | 219 | #define sq(n) ((n) * (n)) 220 | #define sqdist(a, b) (sq((a).x - (b).x) + sq((a).y - (b).y)) 221 | 222 | /* currently only points */ 223 | static PcbItem * 224 | find_closest_item(PcbCoordinate c, long long layer) 225 | { 226 | PcbItem *best = NULL; 227 | PcbItem *cur; 228 | gdouble bestdist = 256.0; /* square of distance (avoid sqrt()) */ 229 | gdouble curdist; 230 | 231 | for (cur = pcb.items; cur; cur = cur->next) { 232 | if (!(cur->layers & layer)) 233 | continue; 234 | switch (cur->type) { 235 | case PCB_POINT: 236 | curdist = sqdist(cur->p, c); 237 | break; 238 | default: 239 | continue; 240 | } 241 | if (curdist < 16.0 && curdist < bestdist) { 242 | best = cur; 243 | bestdist = curdist; 244 | } 245 | } 246 | return best; 247 | } 248 | #define find_closest_point find_closest_item 249 | 250 | static void 251 | select_connected(PcbItem *item) 252 | { 253 | PcbItem *cur; 254 | 255 | if (!item) 256 | return; 257 | if (item->flags & PCB_SELECTED) 258 | return; 259 | select_item(item); 260 | 261 | switch (item->type) { 262 | case PCB_POINT: 263 | for (cur = pcb.items; cur; cur = cur->next) { 264 | if (cur->flags & PCB_SELECTED || cur->type != PCB_LINE) 265 | continue; 266 | if (cur->l.point[0] == item) { 267 | select_item(cur); 268 | select_connected(cur->l.point[1]); 269 | } else if (cur->l.point[1] == item) { 270 | select_item(cur); 271 | select_connected(cur->l.point[0]); 272 | } 273 | } 274 | break; 275 | case PCB_LINE: 276 | select_connected(item->l.point[0]); 277 | select_connected(item->l.point[1]); 278 | break; 279 | } 280 | } 281 | 282 | static PcbItem * 283 | find_line(PcbItem *points[]) 284 | { 285 | PcbItem *cur; 286 | 287 | for (cur = pcb.items; cur; cur = cur->next) { 288 | if (cur->type == PCB_LINE && (cur->layers & CUR_LAYER()) && 289 | (!memcmp(cur->l.point, points, sizeof(*points) * 2) || 290 | (cur->l.point[0] == points[1] && 291 | cur->l.point[1] == points[0]))) 292 | break; 293 | } 294 | return cur; 295 | } 296 | 297 | static void 298 | try_autolimit(PcbItem *point) 299 | { 300 | PcbItem *line = NULL; 301 | PcbItem *cur; 302 | 303 | g_print("try autolimit\n"); 304 | for (cur = pcb.items; cur; cur = cur->next) { 305 | if (cur->type == PCB_LINE && 306 | (cur->l.point[0] == point || cur->l.point[1] == point)) { 307 | if (line) 308 | return; 309 | g_print("found\n"); 310 | line = cur; 311 | } 312 | } 313 | if (line) 314 | change_layers(point, LAYERS_BETWEEN(line->layers, CUR_LAYER())); 315 | } 316 | 317 | static void 318 | toggle_line(PcbItem *points[]) 319 | { 320 | init_transaction(); 321 | new_action(); 322 | pcb.new_action->layers = CUR_LAYER(); 323 | pcb.new_action->c = points[0]->p; 324 | pcb.new_action->l = points[1]->p; 325 | if (find_line(points)) 326 | pcb.new_action->act = PCB_REMOVE | PCB_LINE; 327 | else { 328 | pcb.new_action->act = PCB_ADD | PCB_LINE; 329 | if (pcb.flags & PCB_AUTOLIMIT) { 330 | try_autolimit(points[0]); 331 | try_autolimit(points[1]); 332 | } 333 | } 334 | commit_transaction(); 335 | } 336 | 337 | static void 338 | keep_tracing(PcbCoordinate c, int flags) 339 | { 340 | long long layers = flags ? CUR_LAYER() : ALL_LAYERS(); 341 | PcbItem *item; 342 | 343 | init_transaction(); 344 | if ((item = find_closest_point(c, layers))) { 345 | c = item->p; 346 | } else { 347 | new_action(); 348 | pcb.new_action->act = PCB_ADD | PCB_POINT; 349 | pcb.new_action->flags = flags; 350 | pcb.new_action->layers = layers; 351 | pcb.new_action->c = c; 352 | } 353 | if (pcb.coords) { 354 | PcbItem *lastitem = find_point(pcb.c[0], ALL_LAYERS()); 355 | select_item(lastitem); 356 | if (pcb.flags & PCB_AUTOLIMIT) 357 | try_autolimit(lastitem); 358 | new_action(); 359 | pcb.new_action->act = PCB_ADD | PCB_LINE; 360 | pcb.new_action->layers = CUR_LAYER(); 361 | pcb.new_action->c = pcb.c[0]; 362 | pcb.new_action->l = c; 363 | } 364 | commit_transaction(); 365 | pcb.c[0] = c; 366 | pcb.coords = 1; 367 | select_item(item ? : find_point(c, ALL_LAYERS())); 368 | } 369 | 370 | static void 371 | try_interconnect(void) 372 | { 373 | PcbItem *points[2]; 374 | PcbItem *cur; 375 | int found = 0; 376 | 377 | if (pcb.selected != 2) 378 | return; 379 | for (cur = pcb.items; cur && found < 2; cur = cur->next) { 380 | if (cur->flags & PCB_SELECTED) 381 | points[found++] = cur; 382 | } 383 | if (found != 2) 384 | return; 385 | toggle_line(points); 386 | select_item(points[0]); 387 | select_item(points[1]); 388 | } 389 | 390 | static void 391 | change_mode(int mode) 392 | { 393 | static char *desc[] = { 394 | "select", 395 | "add point", 396 | "add via", 397 | "toggle line", 398 | "examine", 399 | "trace line", 400 | }; 401 | 402 | deselect_all(0); 403 | pcb.mode = mode; 404 | g_print("%s mode\n", desc[mode]); 405 | } 406 | 407 | #define BOTTOM 0 408 | #define DOWN 1 409 | #define UP 2 410 | #define TOP 3 411 | 412 | static void 413 | switch_layer(int direction) 414 | { 415 | int newlayer = pcb.curlayer; 416 | 417 | switch (direction) { 418 | case UP: 419 | if (++newlayer == pcb.layers) { 420 | g_print("this is the top layer!\n"); 421 | return; 422 | } 423 | break; 424 | case DOWN: 425 | if (!newlayer--) { 426 | g_print("this is the bottom layer!\n"); 427 | return; 428 | } 429 | break; 430 | case TOP: 431 | newlayer = pcb.layers - 1; 432 | break; 433 | case BOTTOM: 434 | newlayer = 0; 435 | break; 436 | } 437 | if (pcb.mode == PCB_ADD_LINE) 438 | deselect_all(0); 439 | undisplay(); 440 | pcb.curlayer = newlayer; 441 | redisplay(); 442 | g_print("layer %d of %d\n", newlayer, pcb.layers); 443 | } 444 | 445 | #define ZOOM_IN 0 446 | #define ZOOM_OUT 1 447 | #define ZOOM_NORMAL 2 448 | 449 | static void 450 | zoom(int dir) 451 | { 452 | static char *desc[] = { "in", "out", "to normal" }; 453 | int i; 454 | 455 | switch (dir) { 456 | case ZOOM_IN: 457 | pcb.scale *= 2.0; 458 | break; 459 | case ZOOM_OUT: 460 | pcb.scale /= 2.0; 461 | break; 462 | case ZOOM_NORMAL: 463 | pcb.scale = 1.0; 464 | break; 465 | } 466 | for (i = 0; i < pcb.layers; i++) 467 | goo_canvas_set_scale(pcb.layer[i].canvas, pcb.scale); 468 | g_print("zoom %s: scale %f\n", desc[dir], pcb.scale); 469 | undisplay(); 470 | redisplay(); 471 | } 472 | 473 | static void 474 | toggle_overlays(int dummy) 475 | { 476 | (void)dummy; 477 | if (pcb.layer[pcb.curlayer].olay) 478 | hide_overlays(pcb.curlayer); 479 | else 480 | show_overlays(pcb.curlayer); 481 | } 482 | 483 | static void 484 | toggle_autolimit(int dummy) 485 | { 486 | (void)dummy; 487 | pcb.flags ^= PCB_AUTOLIMIT; 488 | g_print("autolimit %s\n", pcb.flags & PCB_AUTOLIMIT ? "on" : "off"); 489 | } 490 | 491 | static void 492 | quit(int force) 493 | { 494 | if (!force && (pcb.flags & PCB_DIRTY)) { 495 | g_warning("dirty, not quitting ('w' to save, 'Q' to quit)\n"); 496 | return; 497 | } 498 | gtk_main_quit(); 499 | } 500 | 501 | typedef struct { 502 | unsigned int keyval; 503 | void (*func)(int); 504 | int param; 505 | char *desc; 506 | } PcbKeyBinding; 507 | 508 | static void print_help(int); 509 | 510 | PcbKeyBinding key_binding[] = { 511 | { GDK_a, toggle_autolimit, 0, 512 | "a\tToggle autolimiting of vias to layers between two wires\n" }, 513 | { GDK_c, change_layers_selected, 0, 514 | "c\tIn select mode, change layers of selected items\n" }, 515 | { GDK_d, delete_selected, 0, 516 | "d\tIn select mode, delete selected items\n" }, 517 | { GDK_e, change_mode, PCB_EXAMINE, 518 | "e\tEnter examine mode: click point to show connections\n" }, 519 | { GDK_h, print_help, 0, 520 | "h\tPrint this help\n" }, 521 | { GDK_i, toggle_poi_selected, 0, 522 | "i\tIn select mode, toggle point-of-interest on selected items\n" }, 523 | { GDK_l, change_mode, PCB_ADD_LINE, 524 | "l\tEnter toggle line mode: click two points to add/remove wire\n" }, 525 | { GDK_p, change_mode, PCB_ADD_POINT, 526 | "p\tEnter add point mode: click to add a point on current layer\n" }, 527 | { GDK_q, quit, 0, 528 | "q\tQuit, unless project is modified\n" }, 529 | { GDK_Q, quit, 1, 530 | "Q\tQuit, even if project is modified\n" }, 531 | { GDK_r, redo, 0, 532 | "r\tRedo\n" }, 533 | { GDK_s, change_mode, PCB_SELECT, 534 | "s\tEnter select mode: click points to select\n" }, 535 | { GDK_t, change_mode, PCB_TRACE, 536 | "t\tEnter trace mode: click points and change layers to add continuous line\n" }, 537 | { GDK_u, undo, 0, 538 | "u\tUndo\n" }, 539 | { GDK_v, change_mode, PCB_ADD_VIA, 540 | "v\tEnter add via mode: click to add a point on all layers\n" }, 541 | { GDK_w, save_project, 0, 542 | "w\tWrite (save) project\n" }, 543 | { GDK_x, toggle_overlays, 0, 544 | "x\tShow/hide overlays (you can't do much with overlays hidden)\n" }, 545 | { GDK_plus, zoom, ZOOM_IN, 546 | "+\tZoom in\n" }, 547 | { GDK_minus, zoom, ZOOM_OUT, 548 | "-\tZoom out\n" }, 549 | { GDK_0, zoom, ZOOM_NORMAL, 550 | "0\tZoom to 100%\n" }, 551 | { GDK_Up, switch_layer, UP, 552 | "Up\tGo up a layer\n" }, 553 | { GDK_Down, switch_layer, DOWN, 554 | "Down\tGo down a layer\n" }, 555 | { GDK_Home, switch_layer, TOP, 556 | "Home\tGo to first layer\n" }, 557 | { GDK_End, switch_layer, BOTTOM, 558 | "End\tGo to last layer\n" }, 559 | { GDK_Escape, deselect_all, 0, 560 | "Esc\tDeselect all\n" }, 561 | { 0, NULL, 0, 562 | NULL }, 563 | }; 564 | 565 | static void 566 | print_help(int dummy) 567 | { 568 | PcbKeyBinding *cur; 569 | 570 | (void)dummy; 571 | g_print("\n\nKeyboard:\n"); 572 | for (cur = key_binding; cur->desc; cur++) 573 | g_print("%s", cur->desc); 574 | g_print("\n\n"); 575 | } 576 | 577 | static gboolean 578 | key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) 579 | { 580 | PcbKeyBinding *cur; 581 | 582 | (void)widget; 583 | (void)data; 584 | // g_print("event->state %x, event->keyval %x\n", 585 | // event->state, event->keyval); 586 | // if (!(event->state & ~(GDK_SHIFT_MASK | GDK_LOCK_MASK))) { 587 | for (cur = key_binding; cur->keyval; cur++) 588 | if (cur->keyval == event->keyval) { 589 | (*cur->func)(cur->param); 590 | break; 591 | } 592 | // } 593 | return TRUE; 594 | } 595 | 596 | static gboolean 597 | btn_press(GooCanvasItem *item, GooCanvasItem *target, 598 | GdkEventButton *event, gpointer data) 599 | { 600 | PcbCoordinate c = { event->x, event->y }; 601 | 602 | (void)item; 603 | (void)target; 604 | (void)data; 605 | g_print("button %d @ %f %f\n", event->button, event->x, event->y); 606 | if (!pcb.layer[pcb.curlayer].olay) 607 | return TRUE; 608 | switch (event->button) { 609 | case 1: 610 | switch (pcb.mode) { 611 | case PCB_SELECT: 612 | select_item(find_closest_item(c, CUR_LAYER())); 613 | break; 614 | case PCB_ADD_POINT: 615 | add_point(c, CUR_LAYER()); 616 | break; 617 | case PCB_ADD_VIA: 618 | add_point(c, ALL_LAYERS()); 619 | break; 620 | case PCB_ADD_LINE: 621 | select_item(find_closest_point(c, CUR_LAYER())); 622 | try_interconnect(); 623 | break; 624 | case PCB_EXAMINE: 625 | select_connected(find_closest_item(c, CUR_LAYER())); 626 | break; 627 | case PCB_TRACE: 628 | keep_tracing(c, 0); 629 | break; 630 | } 631 | break; 632 | case 3: 633 | switch (pcb.mode) { 634 | #if 0 635 | case PCB_SELECT: 636 | select_item(find_closest_item(c, CUR_LAYER())); 637 | break; 638 | case PCB_ADD_POINT: 639 | add_point(c, CUR_LAYER()); 640 | break; 641 | case PCB_ADD_VIA: 642 | add_point(c, ALL_LAYERS()); 643 | break; 644 | case PCB_ADD_LINE: 645 | select_item(find_closest_point(c, CUR_LAYER())); 646 | try_interconnect(); 647 | break; 648 | case PCB_EXAMINE: 649 | select_connected(find_closest_item(c, CUR_LAYER())); 650 | break; 651 | #endif 652 | case PCB_TRACE: 653 | keep_tracing(c, PCB_BEND); 654 | break; 655 | } 656 | break; 657 | } 658 | return TRUE; 659 | } 660 | 661 | static gboolean 662 | delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) 663 | { 664 | (void)widget; 665 | (void)event; 666 | (void)data; 667 | 668 | /* If you return FALSE in the "delete-event" signal handler, 669 | * GTK will emit the "destroy" signal. Returning TRUE means 670 | * you don't want the window to be destroyed. 671 | * This is useful for popping up 'are you sure you want to quit?' 672 | * type dialogs. */ 673 | 674 | g_print("delete event occurred\n"); 675 | 676 | /* Change TRUE to FALSE and the main window will be destroyed with 677 | * a "delete-event". */ 678 | 679 | return FALSE; 680 | } 681 | 682 | static void 683 | destroy(GtkWidget *widget, gpointer data) 684 | { 685 | (void)widget; 686 | (void)data; 687 | g_print("destroy\n"); 688 | gtk_main_quit(); 689 | } 690 | 691 | void 692 | init_layer_events(GooCanvasItem *w) 693 | { 694 | g_signal_connect(w, "button_press_event", G_CALLBACK(btn_press), NULL); 695 | } 696 | 697 | void 698 | init_window_events(GtkWidget *w) 699 | { 700 | g_signal_connect(w, "key_press_event", G_CALLBACK(key_press), NULL); 701 | g_signal_connect(w, "delete-event", G_CALLBACK(delete_event), NULL); 702 | g_signal_connect(w, "destroy", G_CALLBACK(destroy), NULL); 703 | } 704 | --------------------------------------------------------------------------------